roboticstoolbox-python 1.4.0__py3-none-any.whl

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 (610) hide show
  1. roboticstoolbox/__init__.py +107 -0
  2. roboticstoolbox/backends/Connector.py +113 -0
  3. roboticstoolbox/backends/Dynamixel/README.md +9 -0
  4. roboticstoolbox/backends/Dynamixel/dynamixel.json +581 -0
  5. roboticstoolbox/backends/Dynamixel/dynamixel_io.py +450 -0
  6. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/LICENSE +201 -0
  7. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/README.md +28 -0
  8. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/ReleaseNote.md +181 -0
  9. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/__init__.py +27 -0
  10. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_bulk_read.py +163 -0
  11. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_bulk_write.py +109 -0
  12. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_sync_read.py +166 -0
  13. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_sync_write.py +99 -0
  14. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/packet_handler.py +33 -0
  15. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/port_handler.py +155 -0
  16. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/protocol1_packet_handler.py +548 -0
  17. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/protocol2_packet_handler.py +1080 -0
  18. roboticstoolbox/backends/Dynamixel/dynamixel_sdk/robotis_def.py +75 -0
  19. roboticstoolbox/backends/Dynamixel/dyndata.py +121 -0
  20. roboticstoolbox/backends/PyPlot/EllipsePlot.py +256 -0
  21. roboticstoolbox/backends/PyPlot/PyPlot.py +778 -0
  22. roboticstoolbox/backends/PyPlot/PyPlot2.py +537 -0
  23. roboticstoolbox/backends/PyPlot/README.md +67 -0
  24. roboticstoolbox/backends/PyPlot/RobotPlot.py +247 -0
  25. roboticstoolbox/backends/PyPlot/RobotPlot2.py +123 -0
  26. roboticstoolbox/backends/PyPlot/__init__.py +4 -0
  27. roboticstoolbox/backends/ROS/ROS.py +129 -0
  28. roboticstoolbox/backends/ROS/__init__.py +3 -0
  29. roboticstoolbox/backends/__init__.py +39 -0
  30. roboticstoolbox/backends/swift/__init__.py +165 -0
  31. roboticstoolbox/bin/__init__.py +0 -0
  32. roboticstoolbox/bin/_bintools.py +61 -0
  33. roboticstoolbox/bin/rtbtool.py +404 -0
  34. roboticstoolbox/blocks/Icons/250x250/armplot.png +0 -0
  35. roboticstoolbox/blocks/Icons/250x250/bicycle.png +0 -0
  36. roboticstoolbox/blocks/Icons/250x250/camera.png +0 -0
  37. roboticstoolbox/blocks/Icons/250x250/circlepath.png +0 -0
  38. roboticstoolbox/blocks/Icons/250x250/coriolis.png +0 -0
  39. roboticstoolbox/blocks/Icons/250x250/ctraj.png +0 -0
  40. roboticstoolbox/blocks/Icons/250x250/delta2tr.png +0 -0
  41. roboticstoolbox/blocks/Icons/250x250/diffsteer.png +0 -0
  42. roboticstoolbox/blocks/Icons/250x250/fdyn.png +0 -0
  43. roboticstoolbox/blocks/Icons/250x250/fdynx.png +0 -0
  44. roboticstoolbox/blocks/Icons/250x250/fkine.png +0 -0
  45. roboticstoolbox/blocks/Icons/250x250/gravload.png +0 -0
  46. roboticstoolbox/blocks/Icons/250x250/idyn.png +0 -0
  47. roboticstoolbox/blocks/Icons/250x250/idynx.png +0 -0
  48. roboticstoolbox/blocks/Icons/250x250/ikine.png +0 -0
  49. roboticstoolbox/blocks/Icons/250x250/inertia.png +0 -0
  50. roboticstoolbox/blocks/Icons/250x250/jacobian.png +0 -0
  51. roboticstoolbox/blocks/Icons/250x250/jtraj.png +0 -0
  52. roboticstoolbox/blocks/Icons/250x250/lspb.png +0 -0
  53. roboticstoolbox/blocks/Icons/250x250/multirotor.png +0 -0
  54. roboticstoolbox/blocks/Icons/250x250/multirotormixer.png +0 -0
  55. roboticstoolbox/blocks/Icons/250x250/multirotorplot.png +0 -0
  56. roboticstoolbox/blocks/Icons/250x250/point2tr.png +0 -0
  57. roboticstoolbox/blocks/Icons/250x250/tr2delta.png +0 -0
  58. roboticstoolbox/blocks/Icons/250x250/tr2t.png +0 -0
  59. roboticstoolbox/blocks/Icons/250x250/unicycle.png +0 -0
  60. roboticstoolbox/blocks/Icons/250x250/vehicleplot.png +0 -0
  61. roboticstoolbox/blocks/Icons/50x50/armplot.png +0 -0
  62. roboticstoolbox/blocks/Icons/50x50/bicycle.png +0 -0
  63. roboticstoolbox/blocks/Icons/50x50/camera.png +0 -0
  64. roboticstoolbox/blocks/Icons/50x50/circlepath.png +0 -0
  65. roboticstoolbox/blocks/Icons/50x50/coriolis.png +0 -0
  66. roboticstoolbox/blocks/Icons/50x50/delta2tr.png +0 -0
  67. roboticstoolbox/blocks/Icons/50x50/diffsteer.png +0 -0
  68. roboticstoolbox/blocks/Icons/50x50/fdyn.png +0 -0
  69. roboticstoolbox/blocks/Icons/50x50/fdynx.png +0 -0
  70. roboticstoolbox/blocks/Icons/50x50/fkine.png +0 -0
  71. roboticstoolbox/blocks/Icons/50x50/gravload.png +0 -0
  72. roboticstoolbox/blocks/Icons/50x50/idyn.png +0 -0
  73. roboticstoolbox/blocks/Icons/50x50/idynx.png +0 -0
  74. roboticstoolbox/blocks/Icons/50x50/ikine.png +0 -0
  75. roboticstoolbox/blocks/Icons/50x50/inertia.png +0 -0
  76. roboticstoolbox/blocks/Icons/50x50/jacobian.png +0 -0
  77. roboticstoolbox/blocks/Icons/50x50/jtraj.png +0 -0
  78. roboticstoolbox/blocks/Icons/50x50/lspb.png +0 -0
  79. roboticstoolbox/blocks/Icons/50x50/multirotor.png +0 -0
  80. roboticstoolbox/blocks/Icons/50x50/multirotormixer.png +0 -0
  81. roboticstoolbox/blocks/Icons/50x50/multirotorplot.png +0 -0
  82. roboticstoolbox/blocks/Icons/50x50/point2tr.png +0 -0
  83. roboticstoolbox/blocks/Icons/50x50/tr2delta.png +0 -0
  84. roboticstoolbox/blocks/Icons/50x50/tr2t.png +0 -0
  85. roboticstoolbox/blocks/Icons/50x50/unicycle.png +0 -0
  86. roboticstoolbox/blocks/Icons/50x50/vehicleplot.png +0 -0
  87. roboticstoolbox/blocks/Icons/armplot.png +0 -0
  88. roboticstoolbox/blocks/Icons/bicycle.png +0 -0
  89. roboticstoolbox/blocks/Icons/camera.png +0 -0
  90. roboticstoolbox/blocks/Icons/circlepath.png +0 -0
  91. roboticstoolbox/blocks/Icons/coriolis.png +0 -0
  92. roboticstoolbox/blocks/Icons/ctraj.png +0 -0
  93. roboticstoolbox/blocks/Icons/delta2tr.png +0 -0
  94. roboticstoolbox/blocks/Icons/diffsteer.png +0 -0
  95. roboticstoolbox/blocks/Icons/fdyn.png +0 -0
  96. roboticstoolbox/blocks/Icons/fdynx.png +0 -0
  97. roboticstoolbox/blocks/Icons/fkine.png +0 -0
  98. roboticstoolbox/blocks/Icons/gravload.png +0 -0
  99. roboticstoolbox/blocks/Icons/idyn.png +0 -0
  100. roboticstoolbox/blocks/Icons/idynx.png +0 -0
  101. roboticstoolbox/blocks/Icons/ikine.png +0 -0
  102. roboticstoolbox/blocks/Icons/inertia.png +0 -0
  103. roboticstoolbox/blocks/Icons/jacobian.png +0 -0
  104. roboticstoolbox/blocks/Icons/jtraj.png +0 -0
  105. roboticstoolbox/blocks/Icons/lspb.png +0 -0
  106. roboticstoolbox/blocks/Icons/multirotor.png +0 -0
  107. roboticstoolbox/blocks/Icons/multirotormixer.png +0 -0
  108. roboticstoolbox/blocks/Icons/multirotorplot.png +0 -0
  109. roboticstoolbox/blocks/Icons/point2tr.png +0 -0
  110. roboticstoolbox/blocks/Icons/tr2delta.png +0 -0
  111. roboticstoolbox/blocks/Icons/tr2t.png +0 -0
  112. roboticstoolbox/blocks/Icons/unicycle.png +0 -0
  113. roboticstoolbox/blocks/Icons/vehicleplot.png +0 -0
  114. roboticstoolbox/blocks/README.md +43 -0
  115. roboticstoolbox/blocks/__init__.py +6 -0
  116. roboticstoolbox/blocks/arm.py +1591 -0
  117. roboticstoolbox/blocks/mobile.py +522 -0
  118. roboticstoolbox/blocks/quad_model.py +132 -0
  119. roboticstoolbox/blocks/spatial.py +245 -0
  120. roboticstoolbox/blocks/uav.py +949 -0
  121. roboticstoolbox/demo/__init__.py +0 -0
  122. roboticstoolbox/demo/eigdemo.py +112 -0
  123. roboticstoolbox/demo/tripleangledemo.py +239 -0
  124. roboticstoolbox/demo/twistdemo.py +175 -0
  125. roboticstoolbox/ets/ET.py +439 -0
  126. roboticstoolbox/ets/ET2.py +179 -0
  127. roboticstoolbox/ets/ETS.py +2013 -0
  128. roboticstoolbox/ets/ETS2.py +449 -0
  129. roboticstoolbox/ets/_ET.py +668 -0
  130. roboticstoolbox/ets/_ETS.py +781 -0
  131. roboticstoolbox/ets/__init__.py +11 -0
  132. roboticstoolbox/ets/cpp-extensions/Eigen/Cholesky +45 -0
  133. roboticstoolbox/ets/cpp-extensions/Eigen/CholmodSupport +48 -0
  134. roboticstoolbox/ets/cpp-extensions/Eigen/Core +384 -0
  135. roboticstoolbox/ets/cpp-extensions/Eigen/Dense +7 -0
  136. roboticstoolbox/ets/cpp-extensions/Eigen/Eigen +2 -0
  137. roboticstoolbox/ets/cpp-extensions/Eigen/Eigenvalues +60 -0
  138. roboticstoolbox/ets/cpp-extensions/Eigen/Geometry +59 -0
  139. roboticstoolbox/ets/cpp-extensions/Eigen/Householder +29 -0
  140. roboticstoolbox/ets/cpp-extensions/Eigen/IterativeLinearSolvers +48 -0
  141. roboticstoolbox/ets/cpp-extensions/Eigen/Jacobi +32 -0
  142. roboticstoolbox/ets/cpp-extensions/Eigen/KLUSupport +41 -0
  143. roboticstoolbox/ets/cpp-extensions/Eigen/LU +47 -0
  144. roboticstoolbox/ets/cpp-extensions/Eigen/MetisSupport +35 -0
  145. roboticstoolbox/ets/cpp-extensions/Eigen/OrderingMethods +70 -0
  146. roboticstoolbox/ets/cpp-extensions/Eigen/PaStiXSupport +49 -0
  147. roboticstoolbox/ets/cpp-extensions/Eigen/PardisoSupport +35 -0
  148. roboticstoolbox/ets/cpp-extensions/Eigen/QR +50 -0
  149. roboticstoolbox/ets/cpp-extensions/Eigen/QtAlignedMalloc +39 -0
  150. roboticstoolbox/ets/cpp-extensions/Eigen/SPQRSupport +34 -0
  151. roboticstoolbox/ets/cpp-extensions/Eigen/SVD +50 -0
  152. roboticstoolbox/ets/cpp-extensions/Eigen/Sparse +34 -0
  153. roboticstoolbox/ets/cpp-extensions/Eigen/SparseCholesky +37 -0
  154. roboticstoolbox/ets/cpp-extensions/Eigen/SparseCore +69 -0
  155. roboticstoolbox/ets/cpp-extensions/Eigen/SparseLU +50 -0
  156. roboticstoolbox/ets/cpp-extensions/Eigen/SparseQR +36 -0
  157. roboticstoolbox/ets/cpp-extensions/Eigen/StdDeque +27 -0
  158. roboticstoolbox/ets/cpp-extensions/Eigen/StdList +26 -0
  159. roboticstoolbox/ets/cpp-extensions/Eigen/StdVector +27 -0
  160. roboticstoolbox/ets/cpp-extensions/Eigen/SuperLUSupport +64 -0
  161. roboticstoolbox/ets/cpp-extensions/Eigen/UmfPackSupport +40 -0
  162. roboticstoolbox/ets/cpp-extensions/Eigen/src/Cholesky/LDLT.h +688 -0
  163. roboticstoolbox/ets/cpp-extensions/Eigen/src/Cholesky/LLT.h +558 -0
  164. roboticstoolbox/ets/cpp-extensions/Eigen/src/Cholesky/LLT_LAPACKE.h +99 -0
  165. roboticstoolbox/ets/cpp-extensions/Eigen/src/CholmodSupport/CholmodSupport.h +682 -0
  166. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/ArithmeticSequence.h +413 -0
  167. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Array.h +417 -0
  168. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/ArrayBase.h +226 -0
  169. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/ArrayWrapper.h +209 -0
  170. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Assign.h +90 -0
  171. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/AssignEvaluator.h +1010 -0
  172. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Assign_MKL.h +178 -0
  173. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/BandMatrix.h +353 -0
  174. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Block.h +448 -0
  175. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/BooleanRedux.h +162 -0
  176. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/CommaInitializer.h +164 -0
  177. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/ConditionEstimator.h +175 -0
  178. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/CoreEvaluators.h +1741 -0
  179. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/CoreIterators.h +132 -0
  180. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/CwiseBinaryOp.h +183 -0
  181. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/CwiseNullaryOp.h +1001 -0
  182. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/CwiseTernaryOp.h +197 -0
  183. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/CwiseUnaryOp.h +103 -0
  184. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/CwiseUnaryView.h +132 -0
  185. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/DenseBase.h +701 -0
  186. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/DenseCoeffsBase.h +685 -0
  187. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/DenseStorage.h +652 -0
  188. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Diagonal.h +258 -0
  189. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/DiagonalMatrix.h +391 -0
  190. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/DiagonalProduct.h +28 -0
  191. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Dot.h +318 -0
  192. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/EigenBase.h +160 -0
  193. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/ForceAlignedAccess.h +150 -0
  194. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Fuzzy.h +155 -0
  195. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/GeneralProduct.h +465 -0
  196. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/GenericPacketMath.h +1040 -0
  197. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/GlobalFunctions.h +194 -0
  198. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/IO.h +258 -0
  199. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/IndexedView.h +237 -0
  200. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Inverse.h +117 -0
  201. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Map.h +171 -0
  202. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/MapBase.h +310 -0
  203. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/MathFunctions.h +2057 -0
  204. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/MathFunctionsImpl.h +200 -0
  205. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Matrix.h +565 -0
  206. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/MatrixBase.h +547 -0
  207. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/NestByValue.h +85 -0
  208. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/NoAlias.h +109 -0
  209. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/NumTraits.h +335 -0
  210. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/PartialReduxEvaluator.h +232 -0
  211. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/PermutationMatrix.h +605 -0
  212. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/PlainObjectBase.h +1128 -0
  213. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Product.h +191 -0
  214. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/ProductEvaluators.h +1179 -0
  215. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Random.h +218 -0
  216. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Redux.h +515 -0
  217. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Ref.h +381 -0
  218. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Replicate.h +142 -0
  219. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Reshaped.h +454 -0
  220. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/ReturnByValue.h +119 -0
  221. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Reverse.h +217 -0
  222. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Select.h +164 -0
  223. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/SelfAdjointView.h +365 -0
  224. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/SelfCwiseBinaryOp.h +47 -0
  225. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Solve.h +188 -0
  226. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/SolveTriangular.h +235 -0
  227. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/SolverBase.h +168 -0
  228. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/StableNorm.h +251 -0
  229. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/StlIterators.h +463 -0
  230. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Stride.h +116 -0
  231. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Swap.h +68 -0
  232. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Transpose.h +464 -0
  233. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Transpositions.h +386 -0
  234. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/TriangularMatrix.h +1001 -0
  235. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/VectorBlock.h +96 -0
  236. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/VectorwiseOp.h +784 -0
  237. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/Visitor.h +381 -0
  238. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AVX/Complex.h +372 -0
  239. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AVX/MathFunctions.h +228 -0
  240. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AVX/PacketMath.h +1574 -0
  241. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AVX/TypeCasting.h +115 -0
  242. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AVX512/Complex.h +422 -0
  243. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AVX512/MathFunctions.h +362 -0
  244. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AVX512/PacketMath.h +2303 -0
  245. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AVX512/TypeCasting.h +89 -0
  246. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AltiVec/Complex.h +417 -0
  247. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AltiVec/MathFunctions.h +90 -0
  248. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AltiVec/MatrixProduct.h +2937 -0
  249. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AltiVec/MatrixProductCommon.h +221 -0
  250. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AltiVec/MatrixProductMMA.h +629 -0
  251. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/AltiVec/PacketMath.h +2711 -0
  252. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/CUDA/Complex.h +258 -0
  253. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/Default/BFloat16.h +700 -0
  254. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/Default/ConjHelper.h +117 -0
  255. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/Default/GenericPacketMathFunctions.h +1649 -0
  256. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/Default/GenericPacketMathFunctionsFwd.h +110 -0
  257. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/Default/Half.h +942 -0
  258. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/Default/Settings.h +49 -0
  259. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/Default/TypeCasting.h +120 -0
  260. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/GPU/MathFunctions.h +103 -0
  261. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/GPU/PacketMath.h +1685 -0
  262. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/GPU/TypeCasting.h +80 -0
  263. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/HIP/hcc/math_constants.h +23 -0
  264. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/MSA/Complex.h +648 -0
  265. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/MSA/MathFunctions.h +387 -0
  266. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/MSA/PacketMath.h +1233 -0
  267. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/NEON/Complex.h +584 -0
  268. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/NEON/GeneralBlockPanelKernel.h +183 -0
  269. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/NEON/MathFunctions.h +75 -0
  270. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/NEON/PacketMath.h +4587 -0
  271. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/NEON/TypeCasting.h +1419 -0
  272. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SSE/Complex.h +351 -0
  273. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SSE/MathFunctions.h +199 -0
  274. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SSE/PacketMath.h +1505 -0
  275. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SSE/TypeCasting.h +142 -0
  276. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SVE/MathFunctions.h +44 -0
  277. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SVE/PacketMath.h +752 -0
  278. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SVE/TypeCasting.h +49 -0
  279. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SYCL/InteropHeaders.h +232 -0
  280. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SYCL/MathFunctions.h +301 -0
  281. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SYCL/PacketMath.h +670 -0
  282. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SYCL/SyclMemoryModel.h +694 -0
  283. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/SYCL/TypeCasting.h +85 -0
  284. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/ZVector/Complex.h +426 -0
  285. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/ZVector/MathFunctions.h +233 -0
  286. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/arch/ZVector/PacketMath.h +1060 -0
  287. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/functors/AssignmentFunctors.h +177 -0
  288. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/functors/BinaryFunctors.h +541 -0
  289. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/functors/NullaryFunctors.h +189 -0
  290. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/functors/StlFunctors.h +166 -0
  291. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/functors/TernaryFunctors.h +25 -0
  292. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/functors/UnaryFunctors.h +1131 -0
  293. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/GeneralBlockPanelKernel.h +2645 -0
  294. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/GeneralMatrixMatrix.h +517 -0
  295. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h +317 -0
  296. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/GeneralMatrixMatrixTriangular_BLAS.h +145 -0
  297. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/GeneralMatrixMatrix_BLAS.h +124 -0
  298. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/GeneralMatrixVector.h +518 -0
  299. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/GeneralMatrixVector_BLAS.h +136 -0
  300. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/Parallelizer.h +180 -0
  301. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/SelfadjointMatrixMatrix.h +544 -0
  302. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/SelfadjointMatrixMatrix_BLAS.h +295 -0
  303. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/SelfadjointMatrixVector.h +262 -0
  304. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/SelfadjointMatrixVector_BLAS.h +118 -0
  305. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/SelfadjointProduct.h +133 -0
  306. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/SelfadjointRank2Update.h +94 -0
  307. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/TriangularMatrixMatrix.h +472 -0
  308. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/TriangularMatrixMatrix_BLAS.h +317 -0
  309. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/TriangularMatrixVector.h +350 -0
  310. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/TriangularMatrixVector_BLAS.h +255 -0
  311. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/TriangularSolverMatrix.h +337 -0
  312. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/TriangularSolverMatrix_BLAS.h +167 -0
  313. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/products/TriangularSolverVector.h +148 -0
  314. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/BlasUtil.h +583 -0
  315. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/ConfigureVectorization.h +512 -0
  316. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/Constants.h +563 -0
  317. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/DisableStupidWarnings.h +106 -0
  318. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/ForwardDeclarations.h +322 -0
  319. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/IndexedViewHelper.h +186 -0
  320. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/IntegralConstant.h +272 -0
  321. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/MKL_support.h +137 -0
  322. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/Macros.h +1464 -0
  323. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/Memory.h +1163 -0
  324. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/Meta.h +812 -0
  325. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/NonMPL2.h +3 -0
  326. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/ReenableStupidWarnings.h +31 -0
  327. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/ReshapedHelper.h +51 -0
  328. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/StaticAssert.h +221 -0
  329. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/SymbolicIndex.h +293 -0
  330. roboticstoolbox/ets/cpp-extensions/Eigen/src/Core/util/XprHelper.h +856 -0
  331. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/ComplexEigenSolver.h +346 -0
  332. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/ComplexSchur.h +462 -0
  333. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/ComplexSchur_LAPACKE.h +91 -0
  334. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/EigenSolver.h +622 -0
  335. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h +418 -0
  336. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h +226 -0
  337. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/HessenbergDecomposition.h +374 -0
  338. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/MatrixBaseEigenvalues.h +158 -0
  339. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/RealQZ.h +657 -0
  340. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/RealSchur.h +558 -0
  341. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/RealSchur_LAPACKE.h +77 -0
  342. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h +904 -0
  343. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h +87 -0
  344. roboticstoolbox/ets/cpp-extensions/Eigen/src/Eigenvalues/Tridiagonalization.h +561 -0
  345. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/AlignedBox.h +486 -0
  346. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/AngleAxis.h +247 -0
  347. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/EulerAngles.h +114 -0
  348. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/Homogeneous.h +501 -0
  349. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/Hyperplane.h +282 -0
  350. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/OrthoMethods.h +235 -0
  351. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/ParametrizedLine.h +232 -0
  352. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/Quaternion.h +870 -0
  353. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/Rotation2D.h +199 -0
  354. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/RotationBase.h +206 -0
  355. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/Scaling.h +188 -0
  356. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/Transform.h +1563 -0
  357. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/Translation.h +202 -0
  358. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/Umeyama.h +166 -0
  359. roboticstoolbox/ets/cpp-extensions/Eigen/src/Geometry/arch/Geometry_SIMD.h +168 -0
  360. roboticstoolbox/ets/cpp-extensions/Eigen/src/Householder/BlockHouseholder.h +110 -0
  361. roboticstoolbox/ets/cpp-extensions/Eigen/src/Householder/Householder.h +176 -0
  362. roboticstoolbox/ets/cpp-extensions/Eigen/src/Householder/HouseholderSequence.h +545 -0
  363. roboticstoolbox/ets/cpp-extensions/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h +226 -0
  364. roboticstoolbox/ets/cpp-extensions/Eigen/src/IterativeLinearSolvers/BiCGSTAB.h +212 -0
  365. roboticstoolbox/ets/cpp-extensions/Eigen/src/IterativeLinearSolvers/ConjugateGradient.h +229 -0
  366. roboticstoolbox/ets/cpp-extensions/Eigen/src/IterativeLinearSolvers/IncompleteCholesky.h +394 -0
  367. roboticstoolbox/ets/cpp-extensions/Eigen/src/IterativeLinearSolvers/IncompleteLUT.h +453 -0
  368. roboticstoolbox/ets/cpp-extensions/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h +444 -0
  369. roboticstoolbox/ets/cpp-extensions/Eigen/src/IterativeLinearSolvers/LeastSquareConjugateGradient.h +198 -0
  370. roboticstoolbox/ets/cpp-extensions/Eigen/src/IterativeLinearSolvers/SolveWithGuess.h +117 -0
  371. roboticstoolbox/ets/cpp-extensions/Eigen/src/Jacobi/Jacobi.h +483 -0
  372. roboticstoolbox/ets/cpp-extensions/Eigen/src/KLUSupport/KLUSupport.h +358 -0
  373. roboticstoolbox/ets/cpp-extensions/Eigen/src/LU/Determinant.h +117 -0
  374. roboticstoolbox/ets/cpp-extensions/Eigen/src/LU/FullPivLU.h +877 -0
  375. roboticstoolbox/ets/cpp-extensions/Eigen/src/LU/InverseImpl.h +432 -0
  376. roboticstoolbox/ets/cpp-extensions/Eigen/src/LU/PartialPivLU.h +624 -0
  377. roboticstoolbox/ets/cpp-extensions/Eigen/src/LU/PartialPivLU_LAPACKE.h +83 -0
  378. roboticstoolbox/ets/cpp-extensions/Eigen/src/LU/arch/InverseSize4.h +351 -0
  379. roboticstoolbox/ets/cpp-extensions/Eigen/src/MetisSupport/MetisSupport.h +137 -0
  380. roboticstoolbox/ets/cpp-extensions/Eigen/src/OrderingMethods/Amd.h +435 -0
  381. roboticstoolbox/ets/cpp-extensions/Eigen/src/OrderingMethods/Eigen_Colamd.h +1863 -0
  382. roboticstoolbox/ets/cpp-extensions/Eigen/src/OrderingMethods/Ordering.h +153 -0
  383. roboticstoolbox/ets/cpp-extensions/Eigen/src/PaStiXSupport/PaStiXSupport.h +678 -0
  384. roboticstoolbox/ets/cpp-extensions/Eigen/src/PardisoSupport/PardisoSupport.h +545 -0
  385. roboticstoolbox/ets/cpp-extensions/Eigen/src/QR/ColPivHouseholderQR.h +674 -0
  386. roboticstoolbox/ets/cpp-extensions/Eigen/src/QR/ColPivHouseholderQR_LAPACKE.h +97 -0
  387. roboticstoolbox/ets/cpp-extensions/Eigen/src/QR/CompleteOrthogonalDecomposition.h +635 -0
  388. roboticstoolbox/ets/cpp-extensions/Eigen/src/QR/FullPivHouseholderQR.h +713 -0
  389. roboticstoolbox/ets/cpp-extensions/Eigen/src/QR/HouseholderQR.h +434 -0
  390. roboticstoolbox/ets/cpp-extensions/Eigen/src/QR/HouseholderQR_LAPACKE.h +68 -0
  391. roboticstoolbox/ets/cpp-extensions/Eigen/src/SPQRSupport/SuiteSparseQRSupport.h +335 -0
  392. roboticstoolbox/ets/cpp-extensions/Eigen/src/SVD/BDCSVD.h +1366 -0
  393. roboticstoolbox/ets/cpp-extensions/Eigen/src/SVD/JacobiSVD.h +812 -0
  394. roboticstoolbox/ets/cpp-extensions/Eigen/src/SVD/JacobiSVD_LAPACKE.h +91 -0
  395. roboticstoolbox/ets/cpp-extensions/Eigen/src/SVD/SVDBase.h +376 -0
  396. roboticstoolbox/ets/cpp-extensions/Eigen/src/SVD/UpperBidiagonalization.h +414 -0
  397. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCholesky/SimplicialCholesky.h +697 -0
  398. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCholesky/SimplicialCholesky_impl.h +174 -0
  399. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/AmbiVector.h +378 -0
  400. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/CompressedStorage.h +274 -0
  401. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/ConservativeSparseSparseProduct.h +352 -0
  402. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/MappedSparseMatrix.h +67 -0
  403. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseAssign.h +270 -0
  404. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseBlock.h +571 -0
  405. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseColEtree.h +206 -0
  406. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseCompressedBase.h +370 -0
  407. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseCwiseBinaryOp.h +722 -0
  408. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseCwiseUnaryOp.h +150 -0
  409. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseDenseProduct.h +342 -0
  410. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseDiagonalProduct.h +138 -0
  411. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseDot.h +98 -0
  412. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseFuzzy.h +29 -0
  413. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseMap.h +305 -0
  414. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseMatrix.h +1518 -0
  415. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseMatrixBase.h +398 -0
  416. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparsePermutation.h +178 -0
  417. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseProduct.h +181 -0
  418. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseRedux.h +49 -0
  419. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseRef.h +397 -0
  420. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseSelfAdjointView.h +659 -0
  421. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseSolverBase.h +124 -0
  422. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseSparseProductWithPruning.h +198 -0
  423. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseTranspose.h +92 -0
  424. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseTriangularView.h +189 -0
  425. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseUtil.h +186 -0
  426. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseVector.h +478 -0
  427. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/SparseView.h +254 -0
  428. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseCore/TriangularSolver.h +315 -0
  429. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU.h +923 -0
  430. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLUImpl.h +66 -0
  431. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_Memory.h +226 -0
  432. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_Structs.h +110 -0
  433. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_SupernodalMatrix.h +375 -0
  434. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_Utils.h +80 -0
  435. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_column_bmod.h +181 -0
  436. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_column_dfs.h +179 -0
  437. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_copy_to_ucol.h +107 -0
  438. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_gemm_kernel.h +280 -0
  439. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_heap_relax_snode.h +126 -0
  440. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_kernel_bmod.h +130 -0
  441. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_panel_bmod.h +223 -0
  442. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_panel_dfs.h +258 -0
  443. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_pivotL.h +137 -0
  444. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_pruneL.h +136 -0
  445. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseLU/SparseLU_relax_snode.h +83 -0
  446. roboticstoolbox/ets/cpp-extensions/Eigen/src/SparseQR/SparseQR.h +758 -0
  447. roboticstoolbox/ets/cpp-extensions/Eigen/src/StlSupport/StdDeque.h +116 -0
  448. roboticstoolbox/ets/cpp-extensions/Eigen/src/StlSupport/StdList.h +106 -0
  449. roboticstoolbox/ets/cpp-extensions/Eigen/src/StlSupport/StdVector.h +131 -0
  450. roboticstoolbox/ets/cpp-extensions/Eigen/src/StlSupport/details.h +84 -0
  451. roboticstoolbox/ets/cpp-extensions/Eigen/src/SuperLUSupport/SuperLUSupport.h +1025 -0
  452. roboticstoolbox/ets/cpp-extensions/Eigen/src/UmfPackSupport/UmfPackSupport.h +642 -0
  453. roboticstoolbox/ets/cpp-extensions/Eigen/src/misc/Image.h +82 -0
  454. roboticstoolbox/ets/cpp-extensions/Eigen/src/misc/Kernel.h +79 -0
  455. roboticstoolbox/ets/cpp-extensions/Eigen/src/misc/RealSvd2x2.h +55 -0
  456. roboticstoolbox/ets/cpp-extensions/Eigen/src/misc/blas.h +440 -0
  457. roboticstoolbox/ets/cpp-extensions/Eigen/src/misc/lapack.h +152 -0
  458. roboticstoolbox/ets/cpp-extensions/Eigen/src/misc/lapacke.h +16292 -0
  459. roboticstoolbox/ets/cpp-extensions/Eigen/src/misc/lapacke_mangling.h +17 -0
  460. roboticstoolbox/ets/cpp-extensions/Eigen/src/plugins/ArrayCwiseBinaryOps.h +358 -0
  461. roboticstoolbox/ets/cpp-extensions/Eigen/src/plugins/ArrayCwiseUnaryOps.h +696 -0
  462. roboticstoolbox/ets/cpp-extensions/Eigen/src/plugins/BlockMethods.h +1442 -0
  463. roboticstoolbox/ets/cpp-extensions/Eigen/src/plugins/CommonCwiseBinaryOps.h +115 -0
  464. roboticstoolbox/ets/cpp-extensions/Eigen/src/plugins/CommonCwiseUnaryOps.h +177 -0
  465. roboticstoolbox/ets/cpp-extensions/Eigen/src/plugins/IndexedViewMethods.h +262 -0
  466. roboticstoolbox/ets/cpp-extensions/Eigen/src/plugins/MatrixCwiseBinaryOps.h +152 -0
  467. roboticstoolbox/ets/cpp-extensions/Eigen/src/plugins/MatrixCwiseUnaryOps.h +95 -0
  468. roboticstoolbox/ets/cpp-extensions/Eigen/src/plugins/ReshapedMethods.h +149 -0
  469. roboticstoolbox/ets/cpp-extensions/README.md +73 -0
  470. roboticstoolbox/ets/cpp-extensions/fknm_nb.cpp +534 -0
  471. roboticstoolbox/ets/cpp-extensions/ik.cpp +301 -0
  472. roboticstoolbox/ets/cpp-extensions/ik.h +59 -0
  473. roboticstoolbox/ets/cpp-extensions/linalg.cpp +316 -0
  474. roboticstoolbox/ets/cpp-extensions/linalg.h +64 -0
  475. roboticstoolbox/ets/cpp-extensions/methods.cpp +372 -0
  476. roboticstoolbox/ets/cpp-extensions/methods.h +32 -0
  477. roboticstoolbox/ets/cpp-extensions/structs.cpp +24 -0
  478. roboticstoolbox/ets/cpp-extensions/structs.h +62 -0
  479. roboticstoolbox/ets/fknm.py +366 -0
  480. roboticstoolbox/mobile/Animations.py +485 -0
  481. roboticstoolbox/mobile/Bug2.py +455 -0
  482. roboticstoolbox/mobile/CurvaturePolyPlanner.py +179 -0
  483. roboticstoolbox/mobile/DistanceTransformPlanner.py +395 -0
  484. roboticstoolbox/mobile/DstarPlanner.py +591 -0
  485. roboticstoolbox/mobile/DubinsPlanner.py +474 -0
  486. roboticstoolbox/mobile/EKF.py +1617 -0
  487. roboticstoolbox/mobile/LatticePlanner.py +419 -0
  488. roboticstoolbox/mobile/OccGrid.py +613 -0
  489. roboticstoolbox/mobile/PRMPlanner.py +348 -0
  490. roboticstoolbox/mobile/ParticleFilter.py +706 -0
  491. roboticstoolbox/mobile/PlannerBase.py +1009 -0
  492. roboticstoolbox/mobile/PoseGraph.py +544 -0
  493. roboticstoolbox/mobile/QuinticPolyPlanner.py +349 -0
  494. roboticstoolbox/mobile/RRTPlanner.py +359 -0
  495. roboticstoolbox/mobile/ReedsSheppPlanner.py +545 -0
  496. roboticstoolbox/mobile/Vehicle.py +1909 -0
  497. roboticstoolbox/mobile/__init__.py +194 -0
  498. roboticstoolbox/mobile/drivers.py +397 -0
  499. roboticstoolbox/mobile/landmarkmap.py +181 -0
  500. roboticstoolbox/mobile/sensors.py +789 -0
  501. roboticstoolbox/models/DH/AL5D.py +121 -0
  502. roboticstoolbox/models/DH/Ball.py +87 -0
  503. roboticstoolbox/models/DH/Baxter.py +91 -0
  504. roboticstoolbox/models/DH/Cobra600.py +63 -0
  505. roboticstoolbox/models/DH/Coil.py +80 -0
  506. roboticstoolbox/models/DH/Hyper.py +81 -0
  507. roboticstoolbox/models/DH/Hyper3d.py +83 -0
  508. roboticstoolbox/models/DH/IRB140.py +159 -0
  509. roboticstoolbox/models/DH/Jaco.py +102 -0
  510. roboticstoolbox/models/DH/KR5.py +112 -0
  511. roboticstoolbox/models/DH/LWR4.py +80 -0
  512. roboticstoolbox/models/DH/Mico.py +102 -0
  513. roboticstoolbox/models/DH/Orion5.py +91 -0
  514. roboticstoolbox/models/DH/P8.py +80 -0
  515. roboticstoolbox/models/DH/Panda.py +213 -0
  516. roboticstoolbox/models/DH/Planar2.py +69 -0
  517. roboticstoolbox/models/DH/Planar3.py +51 -0
  518. roboticstoolbox/models/DH/Puma560.py +326 -0
  519. roboticstoolbox/models/DH/README.md +216 -0
  520. roboticstoolbox/models/DH/Sawyer.py +85 -0
  521. roboticstoolbox/models/DH/Stanford.py +147 -0
  522. roboticstoolbox/models/DH/TwoLink.py +153 -0
  523. roboticstoolbox/models/DH/UR10.py +124 -0
  524. roboticstoolbox/models/DH/UR3.py +98 -0
  525. roboticstoolbox/models/DH/UR5.py +98 -0
  526. roboticstoolbox/models/DH/Uprighttl.py +24 -0
  527. roboticstoolbox/models/DH/__init__.py +52 -0
  528. roboticstoolbox/models/ETS/Frankie.py +90 -0
  529. roboticstoolbox/models/ETS/GenericSeven.py +54 -0
  530. roboticstoolbox/models/ETS/Omni.py +74 -0
  531. roboticstoolbox/models/ETS/Panda.py +69 -0
  532. roboticstoolbox/models/ETS/Planar2.py +49 -0
  533. roboticstoolbox/models/ETS/Planar_Y.py +65 -0
  534. roboticstoolbox/models/ETS/Puma560.py +70 -0
  535. roboticstoolbox/models/ETS/XYPanda.py +84 -0
  536. roboticstoolbox/models/ETS/__init__.py +20 -0
  537. roboticstoolbox/models/README.md +9 -0
  538. roboticstoolbox/models/URDF/AL5D.py +45 -0
  539. roboticstoolbox/models/URDF/Fetch.py +96 -0
  540. roboticstoolbox/models/URDF/Frankie.py +67 -0
  541. roboticstoolbox/models/URDF/FrankieOmni.py +94 -0
  542. roboticstoolbox/models/URDF/Jaco.py +49 -0
  543. roboticstoolbox/models/URDF/KinovaGen3.py +63 -0
  544. roboticstoolbox/models/URDF/LBR.py +57 -0
  545. roboticstoolbox/models/URDF/PR2.py +66 -0
  546. roboticstoolbox/models/URDF/Panda.py +60 -0
  547. roboticstoolbox/models/URDF/Puma560.py +90 -0
  548. roboticstoolbox/models/URDF/UR10.py +43 -0
  549. roboticstoolbox/models/URDF/UR3.py +43 -0
  550. roboticstoolbox/models/URDF/UR5.py +64 -0
  551. roboticstoolbox/models/URDF/URDFRobot.py +402 -0
  552. roboticstoolbox/models/URDF/Valkyrie.py +110 -0
  553. roboticstoolbox/models/URDF/YuMi.py +107 -0
  554. roboticstoolbox/models/URDF/__init__.py +51 -0
  555. roboticstoolbox/models/URDF/px100.py +48 -0
  556. roboticstoolbox/models/URDF/px150.py +48 -0
  557. roboticstoolbox/models/URDF/rx150.py +48 -0
  558. roboticstoolbox/models/URDF/rx200.py +48 -0
  559. roboticstoolbox/models/URDF/vx300.py +48 -0
  560. roboticstoolbox/models/URDF/vx300s.py +48 -0
  561. roboticstoolbox/models/URDF/wx200.py +48 -0
  562. roboticstoolbox/models/URDF/wx250.py +48 -0
  563. roboticstoolbox/models/URDF/wx250s.py +48 -0
  564. roboticstoolbox/models/__init__.py +7 -0
  565. roboticstoolbox/models/catalog.py +226 -0
  566. roboticstoolbox/robot/BaseRobot.py +2589 -0
  567. roboticstoolbox/robot/DHFactor.py +522 -0
  568. roboticstoolbox/robot/DHLink.py +949 -0
  569. roboticstoolbox/robot/DHRobot.py +2591 -0
  570. roboticstoolbox/robot/Dynamics.py +1537 -0
  571. roboticstoolbox/robot/ELink.py +23 -0
  572. roboticstoolbox/robot/ERobot.py +25 -0
  573. roboticstoolbox/robot/Gripper.py +242 -0
  574. roboticstoolbox/robot/IK.py +1378 -0
  575. roboticstoolbox/robot/Link.py +1557 -0
  576. roboticstoolbox/robot/PoERobot.py +352 -0
  577. roboticstoolbox/robot/Robot.py +2157 -0
  578. roboticstoolbox/robot/RobotKinematics.py +1505 -0
  579. roboticstoolbox/robot/RobotPlottingMPL.py +355 -0
  580. roboticstoolbox/robot/RobotProto.py +156 -0
  581. roboticstoolbox/robot/__init__.py +57 -0
  582. roboticstoolbox/robot/cpp-extensions/README.md +29 -0
  583. roboticstoolbox/robot/cpp-extensions/frne.h +96 -0
  584. roboticstoolbox/robot/cpp-extensions/frne_nb.cpp +237 -0
  585. roboticstoolbox/robot/cpp-extensions/ne.c +495 -0
  586. roboticstoolbox/robot/cpp-extensions/vmath.c +163 -0
  587. roboticstoolbox/robot/cpp-extensions/vmath.h +32 -0
  588. roboticstoolbox/robot/frne.py +51 -0
  589. roboticstoolbox/tools/DHFactor.py +375 -0
  590. roboticstoolbox/tools/Ticker.py +53 -0
  591. roboticstoolbox/tools/__init__.py +54 -0
  592. roboticstoolbox/tools/data.py +187 -0
  593. roboticstoolbox/tools/jsingu.py +51 -0
  594. roboticstoolbox/tools/null.py +48 -0
  595. roboticstoolbox/tools/numerical.py +96 -0
  596. roboticstoolbox/tools/p_servo.py +108 -0
  597. roboticstoolbox/tools/params.py +11 -0
  598. roboticstoolbox/tools/plot.py +109 -0
  599. roboticstoolbox/tools/trajectory.py +1152 -0
  600. roboticstoolbox/tools/types.py +13 -0
  601. roboticstoolbox/tools/urdf/__init__.py +45 -0
  602. roboticstoolbox/tools/urdf/tests/data/ur5.urdf +341 -0
  603. roboticstoolbox/tools/urdf/tests/test_urdf.py +116 -0
  604. roboticstoolbox/tools/urdf/urdf.py +1930 -0
  605. roboticstoolbox/tools/urdf/utils.py +50 -0
  606. roboticstoolbox_python-1.4.0.dist-info/METADATA +664 -0
  607. roboticstoolbox_python-1.4.0.dist-info/RECORD +610 -0
  608. roboticstoolbox_python-1.4.0.dist-info/WHEEL +5 -0
  609. roboticstoolbox_python-1.4.0.dist-info/entry_points.txt +6 -0
  610. roboticstoolbox_python-1.4.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,2589 @@
1
+ #!/usr/bin/env python
2
+
3
+ """
4
+ @author: Jesse Haviland
5
+ @author: Peter Corke
6
+ """
7
+
8
+ from __future__ import annotations
9
+ # import sys
10
+ import io
11
+ from abc import ABC
12
+ from copy import deepcopy
13
+ from functools import lru_cache
14
+ from typing import (
15
+ IO,
16
+ TYPE_CHECKING,
17
+ Any,
18
+ Callable,
19
+ Generic,
20
+ TypeVar,
21
+ cast,
22
+ )
23
+
24
+ from typing_extensions import Literal as L
25
+
26
+ import numpy as np
27
+
28
+ from spatialmath import SE3
29
+ from spatialmath.base.argcheck import (
30
+ getvector,
31
+ getmatrix,
32
+ getunit,
33
+ )
34
+
35
+ from ansitable import ANSITable, Column
36
+ from spatialgeometry import SceneNode
37
+ from roboticstoolbox.backends.Connector import Connector
38
+
39
+ from roboticstoolbox.ets.fknm import Robot_link_T
40
+ import roboticstoolbox as rtb
41
+ from roboticstoolbox.robot.Gripper import Gripper
42
+ from roboticstoolbox.robot.Link import BaseLink, Link
43
+ from roboticstoolbox.ets.ETS import ETS
44
+ from roboticstoolbox.ets.ET import ET
45
+ from roboticstoolbox.robot.Dynamics import DynamicsMixin
46
+ from roboticstoolbox.tools.types import ArrayLike, NDArray
47
+ from roboticstoolbox.tools.params import rtb_get_param
48
+ from roboticstoolbox.robot.RobotPlottingMPL import RobotPlottingMPLMixin
49
+
50
+ if TYPE_CHECKING:
51
+ from matplotlib.cm import Color # pragma nocover
52
+ else:
53
+ Color = None
54
+
55
+ # _default_backend = None
56
+
57
+ # A generic type variable representing any subclass of BaseLink
58
+ LinkType = TypeVar("LinkType", bound=BaseLink)
59
+
60
+ # Link attributes scanned for symbolic (e.g. SymPy) content at build time --
61
+ # DH kinematic parameters plus dynamics parameters; getattr(..., None) below
62
+ # skips whichever of these don't exist on a given Link subclass (e.g. a
63
+ # non-DH Link has no a/alpha/theta/d).
64
+ _SYMBOLIC_LINK_ATTRS = (
65
+ "a", "alpha", "theta", "d", "offset",
66
+ "m", "r", "I", "Jm", "G", "B", "Tc",
67
+ )
68
+
69
+
70
+ def _is_symbolic(value: Any) -> bool:
71
+ """True if value holds non-numeric (e.g. SymPy) content -- same
72
+ object-dtype test used by roboticstoolbox.ets.fknm._is_symbolic."""
73
+ if value is None:
74
+ return False
75
+ return np.asarray(value).dtype == object
76
+
77
+
78
+ class BaseRobot(SceneNode, DynamicsMixin, RobotPlottingMPLMixin, ABC, Generic[LinkType]):
79
+ def __init__(
80
+ self,
81
+ links: list[LinkType],
82
+ gripper_links: LinkType | list[LinkType] | None = None,
83
+ name: str = "",
84
+ manufacturer: str = "",
85
+ comment: str = "",
86
+ base: NDArray | SE3 | None = None,
87
+ tool: NDArray | SE3 | None = None,
88
+ gravity: ArrayLike = [0, 0, -9.81],
89
+ keywords: list[str] | tuple[str, ...] = [],
90
+ symbolic: bool = False,
91
+ configs: dict[str, NDArray] | None = None,
92
+ check_jindex: bool = True,
93
+ ):
94
+ # Initialise the scene node
95
+ SceneNode.__init__(self)
96
+
97
+ # Lets sort out links now
98
+ self._linkdict: dict[str, LinkType] = {}
99
+
100
+ # Sort links and set self.link, self.n, self.base_link,
101
+ # self.ee_links
102
+ self._sort_links(links, gripper_links, check_jindex)
103
+
104
+ # Fix number of links for gripper links
105
+ self._nlinks = len(links)
106
+
107
+ for gripper in self.grippers:
108
+ self._nlinks += len(gripper.links)
109
+
110
+ # Set the pose of the robot in the world frame
111
+ # in the scenenode object to a numpy array
112
+ if isinstance(base, SE3):
113
+ if len(base) != 1:
114
+ raise ValueError("base must be a single SE3, not a batch")
115
+ self._T = cast(NDArray, base.A) # SE3.A typed list|ndarray; single pose always ndarray
116
+ elif isinstance(base, np.ndarray):
117
+ self._T = base
118
+
119
+ # Set the robot tool transform
120
+ if isinstance(tool, SE3):
121
+ if len(tool) != 1:
122
+ raise ValueError("tool must be a single SE3, not a batch")
123
+ self._tool = cast(NDArray, tool.A) # SE3.A typed list|ndarray; single pose always ndarray
124
+ elif isinstance(tool, np.ndarray):
125
+ self._tool = tool
126
+ else:
127
+ self._tool = np.eye(4)
128
+
129
+ # Set the keywords
130
+ if keywords is not None and not isinstance(keywords, (tuple, list)):
131
+ raise TypeError("keywords must be a list or tuple")
132
+ else:
133
+ self._keywords = list(keywords)
134
+
135
+ # Gravity is in the negative-z direction.
136
+ self.gravity = np.array(gravity)
137
+
138
+ # Basic arguments
139
+ self.name = name
140
+ self.manufacturer = manufacturer
141
+ self.comment = comment
142
+ self._symbolic = symbolic
143
+ self._reach = None
144
+ self._hasdynamics = False
145
+ self._hasgeometry = False
146
+ self._hascollision = False
147
+ self._urdf_string = ""
148
+ self._urdf_filepath = ""
149
+
150
+ # Time to checkout the links for geometry information
151
+ auto_symbolic = False
152
+ for link in self.links:
153
+ # Add link back to robot object
154
+ link._robot = self
155
+
156
+ if link.hasdynamics:
157
+ self._hasdynamics = True
158
+ if link.geometry:
159
+ self._hasgeometry = []
160
+ if link.collision:
161
+ self._hascollision = True
162
+
163
+ if isinstance(link, Link):
164
+ if len(link.geometry) > 0:
165
+ self._hasgeometry = True
166
+
167
+ # Detect symbolic (e.g. SymPy) model parameters regardless of
168
+ # whether the caller remembered to pass symbolic=True -- see
169
+ # rne.md issue 3/4: forgetting the flag previously left
170
+ # self.symbolic False even with genuinely symbolic link
171
+ # parameters, which broke rne_python()'s own float64
172
+ # allocation, not just the C dispatch. symbolic= is kept as an
173
+ # override (OR'd in below), not the source of truth.
174
+ if not auto_symbolic:
175
+ for attr in _SYMBOLIC_LINK_ATTRS:
176
+ if _is_symbolic(getattr(link, attr, None)):
177
+ auto_symbolic = True
178
+ break
179
+
180
+ self._symbolic = self._symbolic or auto_symbolic
181
+
182
+ # Current joint configuraiton, velocity, acceleration
183
+ self.q = np.zeros(self.n)
184
+ self.qd = np.zeros(self.n)
185
+ self.qdd = np.zeros(self.n)
186
+
187
+ # The current control mode of the robot
188
+ self.control_mode = "v"
189
+
190
+ # Set up named configuration property
191
+ if configs is None:
192
+ configs = dict()
193
+ self._configs = configs
194
+
195
+ # A flag for watching dynamics properties
196
+ self._frne_stale = False
197
+
198
+ # Set up qlim
199
+ qlim = np.zeros((2, self.n))
200
+ j = 0
201
+
202
+ for i in range(len(self.links)):
203
+ if self.links[i].isjoint:
204
+ qlim[:, j] = self.links[i].qlim
205
+ j += 1
206
+ self._qlim = qlim
207
+
208
+ self._valid_qlim = False
209
+ for i in range(self.n):
210
+ if any(qlim[:, i] != 0) and not any(np.isnan(qlim[:, i])):
211
+ self._valid_qlim = True
212
+
213
+ # SceneNode, set a reference to the first link
214
+ self.scene_children = [self.links[0]] # type: ignore
215
+
216
+ self._default_backend = None
217
+ self._active_plot_env = None
218
+
219
+ # --------------------------------------------------------------------- #
220
+ # --------- Private Methods ------------------------------------------- #
221
+ # --------------------------------------------------------------------- #
222
+
223
+ def _sort_links(
224
+ self,
225
+ links: list[LinkType],
226
+ gripper_links: LinkType | list[LinkType] | None,
227
+ check_jindex: bool,
228
+ ):
229
+ """
230
+ This method does several things for setting up the links of a robot
231
+
232
+ - Gives each link a unique name if it doesn't have one
233
+ - Assigns each link a parent if it doesn't have one
234
+ - Finds and sets the base link
235
+ - Finds and sets the ee links
236
+ - sets the jindices
237
+ - sets n
238
+ - sets links
239
+
240
+ """
241
+
242
+ # The ordered links
243
+ orlinks: list[LinkType] = []
244
+
245
+ # The end-effector links
246
+ self._ee_links: list[LinkType] = []
247
+
248
+ # Check all the incoming Link objects
249
+ n: int = 0
250
+
251
+ # Make sure each link has a name
252
+ # ------------------------------
253
+ for k, link in enumerate(links):
254
+ if not isinstance(link, BaseLink):
255
+ raise TypeError("links should all be Link subclass")
256
+
257
+ # If link has no name, give it one
258
+ if link.name is None or link.name == "":
259
+ link.name = f"link-{k}"
260
+
261
+ link.number = k + 1
262
+
263
+ # Put it in the link dictionary, check for duplicates
264
+ if link.name in self._linkdict:
265
+ raise ValueError(f"link name {link.name} is not unique")
266
+
267
+ self._linkdict[link.name] = link
268
+
269
+ if link.isjoint:
270
+ n += 1
271
+
272
+ # Resolve parents given by name, within the context of
273
+ # this set of links
274
+ # ----------------------------------------------------
275
+ for link in links:
276
+ if link.parent is None and link.parent_name is not None:
277
+ link.parent = self._linkdict[link.parent_name]
278
+
279
+ if all([link.parent is None for link in links]):
280
+ # No parent links were given, assume they are sequential
281
+ for i in range(len(links) - 1):
282
+ # li = links[i]
283
+ links[i + 1].parent = links[i]
284
+
285
+ # Set the base link
286
+ # -----------------
287
+ for link in links:
288
+ # Is this a base link?
289
+
290
+ if isinstance(link.parent, BaseLink):
291
+ # Update children of this link's parent
292
+ link.parent._children.append(link)
293
+ else:
294
+ try:
295
+ if self._base_link is not None:
296
+ raise ValueError("Multiple base links")
297
+ except AttributeError:
298
+ pass
299
+
300
+ self._base_link = link
301
+
302
+ if not hasattr(self, "_base_link"):
303
+ raise ValueError(
304
+ "Invalid link configuration provided, must have a base link"
305
+ )
306
+
307
+ # Scene node, set links between the links
308
+ # ---------------------------------------
309
+ for link in links:
310
+ if isinstance(link.parent, BaseLink):
311
+ link.scene_parent = link.parent
312
+
313
+ # Set up the gripper, make a list containing the root of all
314
+ # grippers
315
+ # ----------------------------------------------------------
316
+ if gripper_links is None:
317
+ gripper_links = []
318
+
319
+ if not isinstance(gripper_links, list):
320
+ gripper_links = [gripper_links]
321
+
322
+ # An empty list to hold all grippers
323
+ self._grippers = []
324
+
325
+ # Make a gripper object for each gripper
326
+ for link in gripper_links:
327
+ g_links = self.dfs_links(link)
328
+
329
+ # Remove gripper links from the robot
330
+ for g_link in g_links:
331
+ # print(g_link)
332
+ links.remove(g_link)
333
+
334
+ # Save the gripper object
335
+ self._grippers.append(Gripper(g_links, name=link.name))
336
+
337
+ # Subtract the n of the grippers from the n of the robot
338
+ for gripper in self._grippers:
339
+ n -= gripper.n
340
+
341
+ # Set the ee links
342
+ # ----------------
343
+ ee_links: list[LinkType] = []
344
+
345
+ if len(gripper_links) == 0:
346
+ for link in links:
347
+ # Is this a leaf node? and do we not have any grippers
348
+ if link.children is None or len(link.children) == 0:
349
+ # No children, must be an end-effector
350
+ ee_links.append(link)
351
+ else:
352
+ for link in gripper_links:
353
+ # Use the passed in value
354
+ if link.parent is not None:
355
+ ee_links.append(link.parent)
356
+
357
+ self._ee_links = ee_links
358
+
359
+ # Assign the joint indices and sort the links
360
+ # -------------------------------------------
361
+ if all([link.jindex is None or link.ets._auto_jindex for link in links]):
362
+ # No joints have an index
363
+ jindex = [0] # "mutable integer" hack
364
+
365
+ def visit_link(link, jindex):
366
+ # if it's a joint, assign it a jindex and increment it
367
+ if link.isjoint and link in links:
368
+ link.jindex = jindex[0]
369
+ jindex[0] += 1
370
+
371
+ if link in links:
372
+ orlinks.append(link)
373
+
374
+ # visit all links in DFS order
375
+ self.dfs_links(self.base_link, lambda link: visit_link(link, jindex))
376
+
377
+ elif all(
378
+ [
379
+ link.jindex is not None and not link.ets._auto_jindex
380
+ for link in links
381
+ if link.isjoint
382
+ ]
383
+ ):
384
+ # Jindex set on all, check they are unique and contiguous
385
+ if check_jindex:
386
+ jset = set(range(n))
387
+ for link in links:
388
+ if link.isjoint and link.jindex not in jset:
389
+ raise ValueError(
390
+ f"joint index {link.jindex} was repeated or out of range"
391
+ )
392
+ jset -= set([link.jindex])
393
+ if len(jset) > 0: # pragma nocover # is impossible
394
+ raise ValueError(f"joints {jset} were not assigned")
395
+ orlinks = links
396
+ else:
397
+ # must be a mixture of Links with/without jindex
398
+ raise ValueError("all links must have a jindex, or none have a jindex")
399
+
400
+ # Set n
401
+ # -----
402
+ self._n = n
403
+
404
+ # Set links
405
+ # ---------
406
+ self._links = orlinks
407
+
408
+ def dynchanged(self, what: str | None = None):
409
+ """
410
+ Dynamic parameters have changed
411
+
412
+ Called from a property setter to inform the robot that the cache of
413
+ dynamic parameters is invalid.
414
+
415
+ See Also
416
+ --------
417
+ :func:`roboticstoolbox.Link._dirties_frne`
418
+
419
+ """
420
+
421
+ self._frne_stale = True
422
+ if what != "gravity":
423
+ self._hasdynamics = True
424
+
425
+ # --------------------------------------------------------------------- #
426
+ # --------- Magic Methods --------------------------------------------- #
427
+ # --------------------------------------------------------------------- #
428
+
429
+ def __iter__(self):
430
+ self._iter = 0
431
+ return self
432
+
433
+ def __next__(self) -> LinkType:
434
+ if self._iter < len(self.links):
435
+ link = self[self._iter]
436
+ self._iter += 1
437
+ return link
438
+ else:
439
+ raise StopIteration
440
+
441
+ def __getitem__(self, i: int | str) -> LinkType:
442
+ """
443
+ Get link
444
+
445
+ This also supports iterating over each link in the robot object,
446
+ from the base to the tool.
447
+
448
+ :param i: link number or name
449
+ :returns: i'th link or named link
450
+
451
+ Examples
452
+ --------
453
+
454
+ .. runblock:: pycon
455
+
456
+ >>> import roboticstoolbox as rtb
457
+ >>> robot = rtb.models.DH.Puma560()
458
+ >>> print(robot[1]) # print the 2nd link
459
+ >>> print([link.a for link in robot]) # print all the a_j values
460
+
461
+ .. rubric:: Notes
462
+
463
+ ``Robot`` supports link lookup by name,
464
+ eg. ``robot['link1']``
465
+
466
+ """
467
+
468
+ if isinstance(i, int):
469
+ return self._links[i]
470
+ else:
471
+ return self._linkdict[i]
472
+
473
+ def __str__(self) -> str:
474
+ """
475
+ Pretty prints the ETS Model of the robot.
476
+
477
+ :returns: Pretty print of the robot model
478
+
479
+ .. rubric:: Notes
480
+
481
+ - Constant links are shown in blue.
482
+ - End-effector links are prefixed with an @
483
+ - Angles in degrees
484
+ - The robot base frame is denoted as ``BASE`` and is equal to the
485
+ robot's ``base`` attribute.
486
+
487
+ """
488
+
489
+ unicode = rtb_get_param("unicode")
490
+ border = "thin" if unicode else "ascii"
491
+
492
+ table = ANSITable(
493
+ Column("link", headalign="^", colalign=">"),
494
+ Column("link", headalign="^", colalign="<"),
495
+ Column("joint", headalign="^", colalign=">"),
496
+ Column("parent", headalign="^", colalign="<"),
497
+ Column("ETS: parent to link", headalign="^", colalign="<"),
498
+ border=border,
499
+ )
500
+
501
+ for k, link in enumerate(self.links):
502
+ color = "" if link.isjoint else "<<blue>>"
503
+ ee = "@" if link in self.ee_links else ""
504
+ ets = link.ets
505
+ if link.parent is None:
506
+ parent_name = "BASE"
507
+ else:
508
+ parent_name = link.parent.name
509
+ s = ets.__str__(f"q{link.jindex}")
510
+ # if len(s) > 0:
511
+ # op = " \u2295 " if unicode else " * " # \oplus
512
+ # s = op + s
513
+
514
+ if link.isjoint:
515
+ jname = link.jindex
516
+ else:
517
+ jname = ""
518
+ table.row(
519
+ # link.jindex,
520
+ k,
521
+ color + ee + link.name,
522
+ jname,
523
+ parent_name,
524
+ f"{s}",
525
+ )
526
+
527
+ classname = "ERobot"
528
+
529
+ s = f"{classname}: {self.name}"
530
+ if self.manufacturer is not None and len(self.manufacturer) > 0:
531
+ s += f" (by {self.manufacturer})"
532
+ s += f", {self.n} joints ({self.structure})"
533
+ if len(self.grippers) > 0:
534
+ s += (
535
+ f", {len(self.grippers)} gripper{'s' if len(self.grippers) > 1 else ''}"
536
+ )
537
+ if self.nbranches > 1:
538
+ s += f", {self.nbranches} branches"
539
+ if self._hasdynamics:
540
+ s += ", dynamics"
541
+ if any([len(link.geometry) > 0 for link in self.links]):
542
+ s += ", geometry"
543
+ if any([len(link.collision) > 0 for link in self.links]):
544
+ s += ", collision"
545
+ s += "\n"
546
+
547
+ s += str(table)
548
+ s += self.configurations_str(border=border)
549
+
550
+ return s
551
+
552
+ def __repr__(self) -> str:
553
+ return str(self)
554
+
555
+ # --------------------------------------------------------------------- #
556
+ # --------- Properties ------------------------------------------------ #
557
+ # --------------------------------------------------------------------- #
558
+
559
+ @property
560
+ def links(self) -> list[LinkType]:
561
+ """
562
+ Robot links
563
+
564
+ :returns: A list of link objects
565
+
566
+ .. rubric:: Notes
567
+
568
+ It is probably more concise to index the robot object rather
569
+ than the list of links, ie. the following are equivalent:
570
+ - ``robot.links[i]``
571
+ - ``robot[i]``
572
+
573
+ """
574
+
575
+ return self._links
576
+
577
+ @property
578
+ def link_dict(self) -> dict[str, LinkType]:
579
+ return self._linkdict
580
+
581
+ @property
582
+ def grippers(self) -> list[Gripper]:
583
+ """
584
+ Grippers attached to the robot
585
+
586
+ :returns: A list of grippers
587
+ """
588
+
589
+ return self._grippers
590
+
591
+ @property
592
+ def base_link(self) -> LinkType:
593
+ """
594
+ Get the robot base link
595
+
596
+ - ``robot.base_link`` is the robot base link
597
+
598
+ :returns: the first link in the robot tree
599
+ """
600
+
601
+ return self._base_link
602
+
603
+ @property
604
+ def ee_links(self) -> list[LinkType]:
605
+ return self._ee_links
606
+
607
+ @property
608
+ def n(self) -> int:
609
+ """
610
+ Number of joints
611
+
612
+ :returns: Number of joints
613
+
614
+ Examples
615
+ --------
616
+
617
+ .. runblock:: pycon
618
+
619
+ >>> import roboticstoolbox as rtb
620
+ >>> robot = rtb.models.DH.Puma560()
621
+ >>> robot.n
622
+
623
+ See Also
624
+ --------
625
+ :func:`nlinks`
626
+ :func:`nbranches`
627
+
628
+ """
629
+
630
+ return self._n
631
+
632
+ @property
633
+ def nlinks(self):
634
+ """
635
+ Number of links
636
+
637
+ The returned number is the total of both variable joints and
638
+ static links
639
+
640
+ :returns: Number of links
641
+
642
+ Examples
643
+ --------
644
+
645
+ .. runblock:: pycon
646
+
647
+ >>> import roboticstoolbox as rtb
648
+ >>> robot = rtb.models.DH.Puma560()
649
+ >>> robot.nlinks
650
+
651
+ See Also
652
+ --------
653
+ :func:`n`
654
+ :func:`nbranches`
655
+
656
+ """
657
+
658
+ return self._nlinks
659
+
660
+ @property
661
+ def nbranches(self) -> int:
662
+ """
663
+ Number of branches
664
+
665
+ Number of branches in this robot. Computed as the number of links with
666
+ zero children
667
+
668
+ :returns: number of branches in the robot's kinematic tree
669
+
670
+ Examples
671
+ --------
672
+
673
+ .. runblock:: pycon
674
+
675
+ >>> import roboticstoolbox as rtb
676
+ >>> robot = rtb.models.ETS.Panda()
677
+ >>> robot.nbranches
678
+
679
+ See Also
680
+ --------
681
+ :func:`n`
682
+ :func:`nlinks`
683
+ """
684
+
685
+ return sum([link.nchildren == 0 for link in self.links]) + len(self.grippers)
686
+
687
+ # --------------------------------------------------------------------- #
688
+
689
+ @property
690
+ def name(self) -> str:
691
+ """
692
+ Get/set robot name
693
+
694
+ - ``robot.name`` is the robot name
695
+ - ``robot.name = ...`` checks and sets the robot name
696
+
697
+ :param name: the new robot name
698
+ :returns: the current robot name
699
+ """
700
+ return self._name
701
+
702
+ @name.setter
703
+ def name(self, name_new: str):
704
+ self._name = name_new
705
+
706
+ @property
707
+ def comment(self) -> str:
708
+ """
709
+ Get/set robot comment
710
+
711
+ - ``robot.comment`` is the robot comment
712
+ - ``robot.comment = ...`` checks and sets the robot comment
713
+
714
+ :param name: the new robot comment
715
+ :returns: robot comment
716
+ """
717
+ return self._comment
718
+
719
+ @comment.setter
720
+ def comment(self, comment_new: str):
721
+ self._comment = comment_new
722
+
723
+ @property
724
+ def manufacturer(self):
725
+ """
726
+ Get/set robot manufacturer's name
727
+
728
+ - ``robot.manufacturer`` is the robot manufacturer's name
729
+ - ``robot.manufacturer = ...`` checks and sets the manufacturer's name
730
+
731
+ :returns: robot manufacturer's name
732
+ """
733
+ return self._manufacturer
734
+
735
+ @manufacturer.setter
736
+ def manufacturer(self, manufacturer_new):
737
+ self._manufacturer = manufacturer_new
738
+
739
+ @property
740
+ def configs(self) -> dict[str, NDArray]:
741
+ return self._configs
742
+
743
+ @property
744
+ def keywords(self) -> list[str]:
745
+ return self._keywords
746
+
747
+ @property
748
+ def symbolic(self) -> bool:
749
+ return self._symbolic
750
+
751
+ @property
752
+ def hasdynamics(self):
753
+ """
754
+ Robot has dynamic parameters
755
+
756
+ :returns: Robot has dynamic parameters
757
+ :returns: At least one link has associated dynamic parameters.
758
+
759
+ Examples
760
+ --------
761
+
762
+ .. runblock:: pycon
763
+
764
+ >>> import roboticstoolbox as rtb
765
+ >>> robot = rtb.models.DH.Puma560()
766
+ >>> robot.hasdynamics
767
+
768
+ """
769
+
770
+ return self._hasdynamics
771
+
772
+ @property
773
+ def hasgeometry(self):
774
+ """
775
+ Robot has geometry model
776
+
777
+ At least one link has associated mesh to describe its shape.
778
+
779
+ :returns: Robot has geometry model
780
+
781
+ Examples
782
+ --------
783
+
784
+ .. runblock:: pycon
785
+
786
+ >>> import roboticstoolbox as rtb
787
+ >>> robot = rtb.models.DH.Puma560()
788
+ >>> robot.hasgeometry
789
+
790
+ See Also
791
+ --------
792
+ :func:`hascollision`
793
+
794
+ """
795
+
796
+ return self._hasgeometry
797
+
798
+ @property
799
+ def hascollision(self):
800
+ """
801
+ Robot has collision model
802
+
803
+ :returns: Robot has collision model
804
+ :returns: At least one link has associated collision model.
805
+
806
+ Examples
807
+ --------
808
+
809
+ .. runblock:: pycon
810
+
811
+ >>> import roboticstoolbox as rtb
812
+ >>> robot = rtb.models.DH.Puma560()
813
+ >>> robot.hascollision
814
+
815
+ See Also
816
+ --------
817
+ :func:`hasgeometry`
818
+
819
+ """
820
+
821
+ return self._hascollision
822
+
823
+ @property
824
+ def default_backend(self):
825
+ """
826
+ Get default graphical backend
827
+
828
+ - ``robot.default_backend`` Get the default graphical backend, used when
829
+ no explicit backend is passed to ``Robot.plot``.
830
+ - ``robot.default_backend = ...`` Set the default graphical backend, used when
831
+ no explicit backend is passed to ``Robot.plot``. The default set here will
832
+ be overridden if the particular ``Robot`` subclass cannot support it.
833
+
834
+ :returns: backend name
835
+ """
836
+ return self._default_backend
837
+
838
+ @default_backend.setter
839
+ def default_backend(self, be):
840
+ self._default_backend = be
841
+
842
+ @property
843
+ def gravity(self) -> NDArray:
844
+ """
845
+ Get/set default gravitational acceleration (Robot superclass)
846
+
847
+ - ``robot.gravity`` is the default gravitational acceleration
848
+ - ``robot.gravity = ...`` checks and sets default gravitational
849
+ acceleration
850
+
851
+ :param gravity: gravitational acceleration in the world frame,
852
+ downwards gravitational force is equivalent to robot base
853
+ acceleration upwards (positive)
854
+ :returns: gravitational acceleration
855
+
856
+ """
857
+
858
+ return self._gravity
859
+
860
+ @gravity.setter
861
+ def gravity(self, gravity_new: ArrayLike):
862
+ self._gravity = np.array(getvector(gravity_new, 3))
863
+ self.dynchanged()
864
+
865
+ # --------------------------------------------------------------------- #
866
+
867
+ @property
868
+ def q(self) -> NDArray:
869
+ """
870
+ Get/set robot joint configuration
871
+
872
+ - ``robot.q`` is the robot joint configuration
873
+ - ``robot.q = ...`` checks and sets the joint configuration
874
+
875
+ :param q: the new robot joint configuration
876
+ :returns: robot joint configuration
877
+ """
878
+
879
+ return self._q
880
+
881
+ @q.setter
882
+ def q(self, q_new: ArrayLike):
883
+ self._q = np.array(getvector(q_new, self.n))
884
+
885
+ @property
886
+ def qd(self) -> NDArray:
887
+ """
888
+ Get/set robot joint velocity
889
+
890
+ - ``robot.qd`` is the robot joint velocity
891
+ - ``robot.qd = ...`` checks and sets the joint velocity
892
+
893
+ :returns: robot joint velocity
894
+ """
895
+
896
+ return self._qd
897
+
898
+ @qd.setter
899
+ def qd(self, qd_new: ArrayLike):
900
+ self._qd = np.array(getvector(qd_new, self.n))
901
+
902
+ @property
903
+ def qdd(self) -> NDArray:
904
+ """
905
+ Get/set robot joint acceleration
906
+
907
+ - ``robot.qdd`` is the robot joint acceleration
908
+ - ``robot.qdd = ...`` checks and sets the robot joint acceleration
909
+
910
+ :returns: robot joint acceleration
911
+ """
912
+ return self._qdd
913
+
914
+ @qdd.setter
915
+ def qdd(self, qdd_new: ArrayLike):
916
+ self._qdd = np.array(getvector(qdd_new, self.n))
917
+
918
+ @property
919
+ def qlim(self) -> NDArray:
920
+ r"""
921
+ Joint limits
922
+
923
+ Limits are extracted from the link objects. If joints limits are
924
+ not set for:
925
+
926
+ - a revolute joint [-𝜋. 𝜋] is returned
927
+ - a prismatic joint an exception is raised
928
+
929
+ :param qlim: An array of joints limits (2, n)
930
+ :raises ValueError: unset limits for a prismatic joint
931
+ :returns: Array of joint limit values
932
+
933
+ Examples
934
+ --------
935
+
936
+ .. runblock:: pycon
937
+
938
+ >>> import roboticstoolbox as rtb
939
+ >>> robot = rtb.models.DH.Puma560()
940
+ >>> robot.qlim
941
+
942
+ """
943
+
944
+ limits = np.zeros((2, self.n))
945
+ j = 0
946
+
947
+ for link in self.links:
948
+ if link.isrevolute:
949
+ if (
950
+ link.qlim is None
951
+ or link.qlim[0] is None
952
+ or np.any(np.isnan(link.qlim))
953
+ ):
954
+ v = [-np.pi, np.pi]
955
+ else:
956
+ v = link.qlim
957
+ elif link.isprismatic:
958
+ if link.qlim is None:
959
+ raise ValueError("Undefined prismatic joint limit")
960
+ else:
961
+ v = link.qlim
962
+ else:
963
+ # Fixed link
964
+ continue # pragma nocover
965
+
966
+ limits[:, j] = v
967
+ j += 1
968
+
969
+ return limits
970
+
971
+ @qlim.setter
972
+ def qlim(self, new_qlim: ArrayLike):
973
+ new_qlim = np.array(new_qlim)
974
+
975
+ if new_qlim.shape != (2, self.n):
976
+ raise ValueError("new_qlim must be of shape (2, n)")
977
+
978
+ j = 0
979
+ for link in self.links:
980
+ if link.isjoint:
981
+ link.qlim = new_qlim[:, j]
982
+ j += 1
983
+
984
+ @property
985
+ def structure(self) -> str:
986
+ """
987
+ Return the joint structure string
988
+
989
+ A string with one letter per joint: ``R`` for a revolute
990
+ joint, and ``P`` for a prismatic joint.
991
+
992
+ :returns: joint configuration string
993
+
994
+ Examples
995
+ --------
996
+
997
+ .. runblock:: pycon
998
+
999
+ >>> import roboticstoolbox as rtb
1000
+ >>> puma = rtb.models.DH.Puma560()
1001
+ >>> puma.structure
1002
+ >>> stanford = rtb.models.DH.Stanford()
1003
+ >>> stanford.structure
1004
+
1005
+ .. rubric:: Notes
1006
+
1007
+ Fixed joints, that maintain a constant link relative pose,
1008
+ are not included.
1009
+ ``len(self.structure) == self.n``.
1010
+
1011
+ """
1012
+
1013
+ structure = []
1014
+
1015
+ for link in self.links:
1016
+ if link.isrevolute:
1017
+ structure.append("R")
1018
+ elif link.isprismatic:
1019
+ structure.append("P")
1020
+
1021
+ return "".join(structure)
1022
+
1023
+ @property
1024
+ def prismaticjoints(self) -> list[bool]:
1025
+ """
1026
+ Revolute joints as bool array
1027
+
1028
+ :returns: array of joint type, True if prismatic
1029
+
1030
+ Examples
1031
+ --------
1032
+
1033
+ .. runblock:: pycon
1034
+
1035
+ >>> import roboticstoolbox as rtb
1036
+ >>> puma = rtb.models.DH.Puma560()
1037
+ >>> puma.prismaticjoints
1038
+ >>> stanford = rtb.models.DH.Stanford()
1039
+ >>> stanford.prismaticjoints
1040
+
1041
+ .. rubric:: Notes
1042
+
1043
+ Fixed joints, that maintain a constant link relative pose,
1044
+ are not included.
1045
+
1046
+ See Also
1047
+ --------
1048
+ :func:`Link.isprismatic`
1049
+ :func:`revolutejoints`
1050
+
1051
+ """
1052
+
1053
+ return [link.isprismatic for link in self.links if link.isjoint]
1054
+
1055
+ @property
1056
+ def revolutejoints(self) -> list[bool]:
1057
+ """
1058
+ Revolute joints as bool array
1059
+
1060
+ :returns: array of joint type, True if revolute
1061
+
1062
+ Examples
1063
+ --------
1064
+
1065
+ .. runblock:: pycon
1066
+
1067
+ >>> import roboticstoolbox as rtb
1068
+ >>> puma = rtb.models.DH.Puma560()
1069
+ >>> puma.revolutejoints
1070
+ >>> stanford = rtb.models.DH.Stanford()
1071
+ >>> stanford.revolutejoints
1072
+
1073
+ .. rubric:: Notes
1074
+
1075
+ Fixed joints, that maintain a constant link relative pose,
1076
+ are not included.
1077
+
1078
+ See Also
1079
+ --------
1080
+ :func:`Link.isrevolute`
1081
+ :func:`prismaticjoints`
1082
+
1083
+ """
1084
+
1085
+ return [link.isrevolute for link in self.links if link.isjoint]
1086
+
1087
+ @property
1088
+ def control_mode(self) -> str:
1089
+ """
1090
+ Get/set robot control mode
1091
+
1092
+ - ``robot.control_type`` is the robot control mode
1093
+ - ``robot.control_type = ...`` checks and sets the robot control mode
1094
+
1095
+ :param control_mode: the new robot control mode
1096
+ :returns: the current robot control mode
1097
+ """
1098
+
1099
+ return self._control_mode
1100
+
1101
+ @control_mode.setter
1102
+ def control_mode(self, cn: str):
1103
+ if cn == "p" or cn == "v" or cn == "a":
1104
+ self._control_mode = cn
1105
+ else:
1106
+ raise ValueError("Control type must be one of 'p', 'v', or 'a'")
1107
+
1108
+ # --------------------------------------------------------------------- #
1109
+
1110
+ @property
1111
+ def urdf_string(self) -> str:
1112
+ return self._urdf_string
1113
+
1114
+ @property
1115
+ def urdf_filepath(self) -> str:
1116
+ return self._urdf_filepath
1117
+
1118
+ # --------------------------------------------------------------------- #
1119
+
1120
+ @property
1121
+ def tool(self) -> SE3:
1122
+ """
1123
+ Get/set robot tool transform
1124
+
1125
+ - ``robot.tool`` is the robot tool transform as an SE3 object
1126
+ - ``robot._tool`` is the robot tool transform as a numpy array
1127
+ - ``robot.tool = ...`` checks and sets the robot tool transform
1128
+
1129
+ :param tool: the new robot tool transform (as an SE(3))
1130
+ :returns: robot tool transform
1131
+ """
1132
+ return SE3(self._tool, check=False)
1133
+
1134
+ @tool.setter
1135
+ def tool(self, T: SE3 | NDArray):
1136
+ if isinstance(T, SE3):
1137
+ if len(T) != 1:
1138
+ raise ValueError("tool must be a single SE3, not a batch")
1139
+ self._tool = cast(NDArray, T.A) # SE3.A typed list|ndarray; single pose always ndarray
1140
+ else:
1141
+ self._tool = T
1142
+
1143
+ @property
1144
+ def base(self) -> SE3:
1145
+ """
1146
+ Get/set robot base transform
1147
+
1148
+ - ``robot.base`` is the robot base transform
1149
+ - ``robot.base = ...`` checks and sets the robot base transform
1150
+
1151
+ :param base: the new robot base transform
1152
+ :returns: the current robot base transform
1153
+ """
1154
+
1155
+ # return a copy, otherwise somebody with
1156
+ # reference to the base can change it
1157
+
1158
+ # This now returns the Scene Node transform
1159
+ # self._T is a copy of SceneNode.__T
1160
+ return SE3(self._T, check=False)
1161
+
1162
+ @base.setter
1163
+ def base(self, T: NDArray | SE3):
1164
+ if isinstance(self, rtb.Robot):
1165
+ # All 3D robots
1166
+ # Set the SceneNode T
1167
+ if isinstance(T, SE3):
1168
+ if len(T) != 1:
1169
+ raise ValueError("base must be a single SE3, not a batch")
1170
+ self._T = cast(NDArray, T.A) # SE3.A typed list|ndarray; single pose always ndarray
1171
+ else:
1172
+ self._T = T
1173
+
1174
+ # --------------------------------------------------------------------- #
1175
+
1176
+ @lru_cache(maxsize=32)
1177
+ def get_path(
1178
+ self,
1179
+ end: Gripper | LinkType | str | None = None,
1180
+ start: Gripper | LinkType | str | None = None,
1181
+ ) -> tuple[list[LinkType], int, SE3]:
1182
+ """
1183
+ Find a path from start to end
1184
+
1185
+ :param end: end-effector or gripper to compute forward kinematics to
1186
+ :param start: name or reference to a base link, defaults to None
1187
+ :raises ValueError: link not known or ambiguous
1188
+ :returns: the path from start to end
1189
+ :returns: the number of joints in the path
1190
+ :returns: the tool transform present after end
1191
+ """
1192
+
1193
+ def search(
1194
+ start,
1195
+ end,
1196
+ explored: set[LinkType | Link],
1197
+ path: list[LinkType | Link],
1198
+ ) -> list[LinkType | Link] | None:
1199
+ link = self._getlink(start, self.base_link)
1200
+ end = self._getlink(end, self.ee_links[0])
1201
+
1202
+ toplevel = len(path) == 0
1203
+ explored.add(link)
1204
+
1205
+ if link == end:
1206
+ return path
1207
+
1208
+ # unlike regular DFS, the neighbours of the node are its children
1209
+ # and its parent.
1210
+
1211
+ # visit child nodes below start
1212
+ if toplevel:
1213
+ path = [link]
1214
+
1215
+ if link.children is not None:
1216
+ for child in link.children:
1217
+ if child not in explored:
1218
+ path.append(child)
1219
+ p = search(child, end, explored, path)
1220
+ if p is not None:
1221
+ return p
1222
+
1223
+ # We didn't find the node below, keep going up a level, and recursing
1224
+ # down again
1225
+ if toplevel:
1226
+ path = []
1227
+
1228
+ if link.parent is not None:
1229
+ parent = link.parent # go up one level toward the root
1230
+ if parent not in explored:
1231
+ if len(path) == 0:
1232
+ p = search(parent, end, explored, [link])
1233
+ else:
1234
+ path.append(link)
1235
+ p = search(parent, end, explored, path)
1236
+
1237
+ if p is not None and len(p) > 0:
1238
+ return p
1239
+
1240
+ end, start, tool = self._get_limit_links(end=end, start=start)
1241
+
1242
+ path = search(start, end, set(), [])
1243
+
1244
+ if path is None or len(path) == 0:
1245
+ raise ValueError("No path found") # pragma nocover
1246
+ elif path[-1] != end:
1247
+ path.append(end)
1248
+
1249
+ if tool is None:
1250
+ tool = SE3()
1251
+
1252
+ return path, len(path), tool # type: ignore
1253
+
1254
+ @lru_cache(maxsize=32)
1255
+ def _getlink(
1256
+ self,
1257
+ link: LinkType | Gripper | str | None,
1258
+ default: LinkType | Gripper | str | None = None,
1259
+ ) -> LinkType | Link:
1260
+ """
1261
+ Validate reference to Link
1262
+
1263
+ ``robot._getlink(link)`` is a validated reference to a Link within
1264
+ the ERobot ``robot``. If ``link`` is:
1265
+
1266
+ - an ``Link`` reference it is validated as belonging to
1267
+ ``robot``.
1268
+ - a string, then it looked up in the robot's link name dictionary, and
1269
+ a Link reference returned.
1270
+
1271
+ :param link: link
1272
+ :raises ValueError: link does not belong to this ERobot
1273
+ :raises TypeError: bad argument
1274
+ :returns: link reference
1275
+ """
1276
+
1277
+ if link is None:
1278
+ link = default
1279
+
1280
+ if isinstance(link, str):
1281
+ if link in self.link_dict:
1282
+ return self.link_dict[link]
1283
+
1284
+ raise ValueError(f"no link named {link}")
1285
+
1286
+ elif isinstance(link, BaseLink):
1287
+ if link in self.links:
1288
+ return link
1289
+ else:
1290
+ for gripper in self.grippers:
1291
+ if link in gripper.links:
1292
+ return link
1293
+
1294
+ raise ValueError("link not in robot links")
1295
+ elif isinstance(link, Gripper):
1296
+ for gripper in self.grippers:
1297
+ if link is gripper:
1298
+ return gripper.links[0]
1299
+
1300
+ raise ValueError("Gripper not in robot")
1301
+ else:
1302
+ raise TypeError("unknown argument")
1303
+
1304
+ def _find_ets(self, start, end, explored, path) -> ETS | None:
1305
+ """
1306
+ Privade method which will recursively find the ETS of a path
1307
+ see ets()
1308
+ """
1309
+
1310
+ link = self._getlink(start, self.base_link)
1311
+ end = self._getlink(end, self.ee_links[0])
1312
+
1313
+ toplevel = path is None
1314
+ explored.add(link)
1315
+
1316
+ if link == end:
1317
+ return path
1318
+
1319
+ # unlike regular DFS, the neighbours of the node are its children
1320
+ # and its parent.
1321
+
1322
+ # visit child nodes below start
1323
+ if toplevel:
1324
+ path = link.ets
1325
+
1326
+ if link.children is not None:
1327
+ for child in link.children:
1328
+ if child not in explored:
1329
+ p = self._find_ets(child, end, explored, path * child.ets)
1330
+ if p is not None:
1331
+ return p
1332
+
1333
+ # we didn't find the node below, keep going up a level, and recursing
1334
+ # down again
1335
+ if toplevel:
1336
+ path = None
1337
+ if link.parent is not None:
1338
+ parent = link.parent # go up one level toward the root
1339
+ if parent not in explored:
1340
+ if path is None:
1341
+ p = self._find_ets(parent, end, explored, link.ets.inv())
1342
+ else:
1343
+ p = self._find_ets(parent, end, explored, path * link.ets.inv())
1344
+ if p is not None:
1345
+ return p
1346
+
1347
+ def _gripper_ets(self, gripper: Gripper) -> ETS:
1348
+ """
1349
+ Privade method which will find the ETS of a gripper
1350
+
1351
+ """
1352
+
1353
+ return ETS(ET.SE3(gripper.tool))
1354
+
1355
+ @lru_cache(maxsize=32)
1356
+ def _get_limit_links(
1357
+ self,
1358
+ end: Gripper | LinkType | str | None = None,
1359
+ start: Gripper | LinkType | str | None = None,
1360
+ ) -> tuple[LinkType, LinkType, SE3 | None]:
1361
+ """
1362
+ Get and validate an end-effector, and a base link
1363
+
1364
+ Helper method to find or validate an end-effector and base link
1365
+
1366
+ end
1367
+ end-effector or gripper to compute forward kinematics to
1368
+ start
1369
+ name or reference to a base link, defaults to None
1370
+
1371
+ ValueError
1372
+ link not known or ambiguous
1373
+ ValueError
1374
+ [description]
1375
+ TypeError
1376
+ unknown type provided
1377
+
1378
+ :returns: end-effector link
1379
+ :returns: base link
1380
+ :returns: tool transform of gripper if applicable
1381
+ """
1382
+
1383
+ tool = None
1384
+ if end is None:
1385
+ if len(self.grippers) > 1:
1386
+ end_ret = self.grippers[0].links[0]
1387
+ tool = self.grippers[0].tool
1388
+ if len(self.grippers) > 1:
1389
+ # Warn user: more than one gripper
1390
+ print("More than one gripper present, using robot.grippers[0]")
1391
+ elif len(self.grippers) == 1:
1392
+ end_ret = self.grippers[0].links[0]
1393
+ tool = self.grippers[0].tool
1394
+
1395
+ # no grippers, use ee link if just one
1396
+ elif len(self.ee_links) > 1:
1397
+ end_ret = self.ee_links[0]
1398
+ if len(self.ee_links) > 1:
1399
+ # Warn user: more than one EE
1400
+ print("More than one end-effector present, using robot.ee_links[0]")
1401
+ else:
1402
+ end_ret = self.ee_links[0]
1403
+
1404
+ else:
1405
+ # Check if end corresponds to gripper
1406
+ for gripper in self.grippers:
1407
+ if end == gripper or end == gripper.name:
1408
+ tool = gripper.tool
1409
+ # end_ret = gripper.links[0]
1410
+
1411
+ # otherwise check for end in the links
1412
+ end_ret = self._getlink(end)
1413
+
1414
+ if start is None:
1415
+ start_ret = self.base_link
1416
+
1417
+ # Cache result
1418
+ self._cache_start = start
1419
+ else:
1420
+ # start effector is specified
1421
+ start_ret = self._getlink(start)
1422
+
1423
+ # because Gripper returns Link not LinkType
1424
+ return end_ret, start_ret, tool # type: ignore
1425
+
1426
+ @lru_cache(maxsize=32)
1427
+ def ets(
1428
+ self,
1429
+ start: LinkType | Gripper | str | None = None,
1430
+ end: LinkType | Gripper | str | None = None,
1431
+ ) -> ETS:
1432
+ """
1433
+ Robot to ETS
1434
+
1435
+ ``robot.ets()`` is an ETS representing the kinematics from base to
1436
+ end-effector.
1437
+
1438
+ ``robot.ets(end=link)`` is an ETS representing the kinematics from
1439
+ base to the link ``link`` specified as a Link reference or a name.
1440
+
1441
+ ``robot.ets(start=l1, end=l2)`` is an ETS representing the kinematics
1442
+ from link ``l1`` to link ``l2``.
1443
+
1444
+ :param :param start: start of path, defaults to ``base_link``:
1445
+ :param :param end: end of path, defaults to end-effector:
1446
+ :raises ValueError: a link does not belong to this ERobot
1447
+ :raises TypeError: a bad link argument
1448
+ :returns: elementary transform sequence
1449
+
1450
+ Examples
1451
+ --------
1452
+
1453
+ .. runblock:: pycon
1454
+
1455
+ >>> import roboticstoolbox as rtb
1456
+ >>> panda = rtb.models.ETS.Panda()
1457
+ >>> panda.ets()
1458
+
1459
+ """
1460
+
1461
+ # ets to stand and end incase of grippers
1462
+ ets_init = None
1463
+ ets_end = None
1464
+
1465
+ if isinstance(start, Gripper):
1466
+ ets_init = self._gripper_ets(start).inv()
1467
+ link = start.links[0].parent
1468
+ if link is None: # pragma nocover
1469
+ raise ValueError("Invalid robot link configuration")
1470
+ else:
1471
+ link = self._getlink(start, self.base_link)
1472
+
1473
+ if end is None:
1474
+ if len(self.grippers) > 1:
1475
+ end_link = self.grippers[0].links[0]
1476
+ ets_end = self._gripper_ets(self.grippers[0])
1477
+ print("multiple grippers present, ambiguous, using self.grippers[0]")
1478
+ elif len(self.grippers) == 1:
1479
+ end_link = self.grippers[0].links[0]
1480
+ ets_end = self._gripper_ets(self.grippers[0])
1481
+ elif len(self.ee_links) > 1:
1482
+ end_link = self._getlink(end, self.ee_links[0])
1483
+ print(
1484
+ "multiple end-effectors present, ambiguous, using self.ee_links[0]"
1485
+ )
1486
+ else:
1487
+ end_link = self._getlink(end, self.ee_links[0])
1488
+ else:
1489
+ if isinstance(end, Gripper):
1490
+ ets_end = self._gripper_ets(end)
1491
+ end_link = end.links[0].parent # type: ignore
1492
+ if end_link is None: # pragma nocover
1493
+ raise ValueError("Invalid robot link configuration")
1494
+ else:
1495
+ end_link = self._getlink(end, self.ee_links[0])
1496
+
1497
+ explored = set()
1498
+
1499
+ if link is end_link:
1500
+ ets = link.ets
1501
+ else:
1502
+ ets = self._find_ets(link, end_link, explored, path=None)
1503
+
1504
+ if ets is None:
1505
+ raise ValueError(
1506
+ "Could not find the requested ETS in this robot"
1507
+ ) # pragma nocover
1508
+
1509
+ if ets_init is not None:
1510
+ ets = ets_init * ets
1511
+
1512
+ if ets_end is not None:
1513
+ ets = ets * ets_end
1514
+
1515
+ return ets
1516
+
1517
+ def copy(self):
1518
+ return deepcopy(self)
1519
+
1520
+ def __deepcopy__(self, memo):
1521
+ links = []
1522
+
1523
+ # if isinstance(self, rtb.DHRobot):
1524
+ # cls = rtb.DHRobot
1525
+ if isinstance(self, rtb.Robot2):
1526
+ cls = rtb.Robot2
1527
+ else:
1528
+ cls = rtb.Robot
1529
+
1530
+ for link in self.links:
1531
+ links.append(deepcopy(link))
1532
+
1533
+ name = deepcopy(self.name)
1534
+ manufacturer = deepcopy(self.manufacturer)
1535
+ comment = deepcopy(self.comment)
1536
+ base = deepcopy(self.base)
1537
+ tool = deepcopy(self.tool)
1538
+ gravity = deepcopy(self.gravity)
1539
+ keywords = deepcopy(self.keywords)
1540
+ symbolic = deepcopy(self.symbolic)
1541
+ configs = deepcopy(self.configs)
1542
+
1543
+ result = cls(
1544
+ links,
1545
+ name=name,
1546
+ manufacturer=manufacturer,
1547
+ comment=comment,
1548
+ base=base, # type: ignore
1549
+ tool=tool,
1550
+ gravity=gravity,
1551
+ keywords=keywords,
1552
+ symbolic=symbolic,
1553
+ configs=configs,
1554
+ )
1555
+
1556
+ # if a configuration was an attribute of original robot, make it an
1557
+ # attribute of the deep copy
1558
+ for config in configs:
1559
+ if hasattr(self, config):
1560
+ setattr(result, config, configs[config])
1561
+
1562
+ memo[id(self)] = result
1563
+ return result
1564
+
1565
+ # --------------------------------------------------------------------- #
1566
+
1567
+ def todegrees(self, q) -> NDArray:
1568
+ """
1569
+ Convert joint angles to degrees
1570
+
1571
+ :param q: The joint configuration of the robot
1572
+ :returns: a vector of joint coordinates in degrees and metres
1573
+ :returns: ``robot.todegrees(q)`` converts joint coordinates ``q`` to degrees
1574
+ :returns: taking into account whether elements of ``q`` correspond to revolute
1575
+ :returns: or prismatic joints, ie. prismatic joint values are not converted.
1576
+ :returns: If ``q`` is a matrix, with one column per joint, the conversion is
1577
+ :returns: performed columnwise.
1578
+
1579
+ Examples
1580
+ --------
1581
+
1582
+ .. runblock:: pycon
1583
+
1584
+ >>> import roboticstoolbox as rtb
1585
+ >>> from math import pi
1586
+ >>> stanford = rtb.models.DH.Stanford()
1587
+ >>> stanford.todegrees([pi/4, pi/8, 2, -pi/4, pi/6, pi/3])
1588
+
1589
+ """
1590
+
1591
+ q = getmatrix(q, (None, self.n))
1592
+
1593
+ for j, revolute in enumerate(self.revolutejoints):
1594
+ if revolute:
1595
+ q[:, j] *= 180.0 / np.pi
1596
+
1597
+ if q.shape[0] == 1:
1598
+ return q[0]
1599
+ else:
1600
+ return q
1601
+
1602
+ def toradians(self, q) -> NDArray:
1603
+ """
1604
+ Convert joint angles to radians
1605
+
1606
+ ``robot.toradians(q)`` converts joint coordinates ``q`` to radians
1607
+ taking into account whether elements of ``q`` correspond to revolute
1608
+ or prismatic joints, ie. prismatic joint values are not converted.
1609
+
1610
+ If ``q`` is a matrix, with one column per joint, the conversion is
1611
+ performed columnwise.
1612
+
1613
+ :param q: The joint configuration of the robot
1614
+ :returns: a vector of joint coordinates in radians and metres
1615
+
1616
+ Examples
1617
+ --------
1618
+
1619
+ .. runblock:: pycon
1620
+
1621
+ >>> import roboticstoolbox as rtb
1622
+ >>> stanford = rtb.models.DH.Stanford()
1623
+ >>> stanford.toradians([10, 20, 2, 30, 40, 50])
1624
+
1625
+ """
1626
+
1627
+ q = getmatrix(q, (None, self.n))
1628
+
1629
+ for j, revolute in enumerate(self.revolutejoints):
1630
+ if revolute:
1631
+ q[:, j] *= np.pi / 180.0
1632
+
1633
+ if q.shape[0] == 1:
1634
+ return q[0]
1635
+ else:
1636
+ return q
1637
+
1638
+ def isrevolute(self, j) -> bool:
1639
+ """
1640
+ Check if joint is revolute
1641
+
1642
+ :returns: True if revolute
1643
+
1644
+ Examples
1645
+ --------
1646
+
1647
+ .. runblock:: pycon
1648
+
1649
+ >>> import roboticstoolbox as rtb
1650
+ >>> puma = rtb.models.DH.Puma560()
1651
+ >>> puma.revolutejoints
1652
+ >>> stanford = rtb.models.DH.Stanford()
1653
+ >>> stanford.isrevolute(1)
1654
+
1655
+ See Also
1656
+ --------
1657
+ :func:`Link.isrevolute`
1658
+ :func:`revolutejoints`
1659
+
1660
+ """
1661
+ return self.revolutejoints[j]
1662
+
1663
+ def isprismatic(self, j) -> bool:
1664
+ """
1665
+ Check if joint is prismatic
1666
+
1667
+ :returns: True if prismatic
1668
+
1669
+ Examples
1670
+ --------
1671
+
1672
+ .. runblock:: pycon
1673
+
1674
+ >>> import roboticstoolbox as rtb
1675
+ >>> puma = rtb.models.DH.Puma560()
1676
+ >>> puma.prismaticjoints
1677
+ >>> stanford = rtb.models.DH.Stanford()
1678
+ >>> stanford.isprismatic(1)
1679
+
1680
+ See Also
1681
+ --------
1682
+ :func:`Link.isprismatic`
1683
+ :func:`prismaticjoints`
1684
+
1685
+ """
1686
+
1687
+ return self.prismaticjoints[j]
1688
+
1689
+ # --------------------------------------------------------------------- #
1690
+
1691
+ def dfs_links(
1692
+ self,
1693
+ start: LinkType,
1694
+ func: Callable[[LinkType | None, Any]] = None,
1695
+ ) -> list[LinkType]:
1696
+ """
1697
+ A link search method
1698
+
1699
+ Visit all links from start in depth-first order and will apply
1700
+ func to each visited link
1701
+
1702
+ :param start: The link to start at
1703
+ :param func: An optional function to apply to each link as it is found
1704
+ :returns: A list of links
1705
+ """
1706
+
1707
+ visited = []
1708
+
1709
+ def vis_children(link):
1710
+ visited.append(link)
1711
+ if func is not None:
1712
+ func(link)
1713
+
1714
+ for li in link.children:
1715
+ if li not in visited:
1716
+ vis_children(li)
1717
+
1718
+ vis_children(start)
1719
+
1720
+ return visited
1721
+
1722
+ def addconfiguration_attr(self, name: str, q: ArrayLike, unit: str = "rad"):
1723
+ """
1724
+ Add a named joint configuration as an attribute
1725
+
1726
+ :param name: Name of the joint configuration
1727
+ :param q: Joint configuration
1728
+
1729
+ Examples
1730
+ --------
1731
+
1732
+ .. runblock:: pycon
1733
+
1734
+ >>> import roboticstoolbox as rtb
1735
+ >>> robot = rtb.models.DH.Puma560()
1736
+ >>> robot.addconfiguration_attr("mypos", [0.1, 0.2, 0.3, 0.4, 0.5, 0.6])
1737
+ >>> robot.mypos
1738
+ >>> robot.configs["mypos"]
1739
+
1740
+ .. rubric:: Notes
1741
+
1742
+ - Used in robot model init method to store the ``qr`` configuration
1743
+ - Dynamically adding attributes to objects can cause issues with
1744
+ Python type checking.
1745
+ - Configuration is also added to the robot instance's dictionary of
1746
+ named configurations.
1747
+
1748
+ See Also
1749
+ --------
1750
+ :meth:`addconfiguration`
1751
+
1752
+ """
1753
+
1754
+ v = getunit(q, unit, dim=self.n)
1755
+ self._configs[name] = v
1756
+ setattr(self, name, v)
1757
+
1758
+ def addconfiguration(self, name: str, q: ArrayLike):
1759
+ """
1760
+ Add a named joint configuration
1761
+
1762
+ Add a named configuration to the robot instance's dictionary of named
1763
+ configurations.
1764
+
1765
+ :param name: Name of the joint configuration
1766
+ :param q: Joint configuration
1767
+
1768
+ Examples
1769
+ --------
1770
+
1771
+ .. runblock:: pycon
1772
+
1773
+ >>> import roboticstoolbox as rtb
1774
+ >>> robot = rtb.models.DH.Puma560()
1775
+ >>> robot.addconfiguration_attr("mypos", [0.1, 0.2, 0.3, 0.4, 0.5, 0.6])
1776
+ >>> robot.configs["mypos"]
1777
+
1778
+ See Also
1779
+ --------
1780
+ :meth:`addconfiguration`
1781
+
1782
+ """
1783
+
1784
+ self._configs[name] = np.array(getvector(q, self.n))
1785
+
1786
+ def configurations_str(self, border="thin"):
1787
+ deg = 180 / np.pi
1788
+
1789
+ # TODO: factor this out of DHRobot
1790
+ def angle(theta, fmt=None):
1791
+ if fmt is not None:
1792
+ try:
1793
+ return fmt.format(theta * deg) + "\u00b0"
1794
+ except TypeError: # pragma nocover
1795
+ pass
1796
+
1797
+ return str(theta * deg) + "\u00b0" # pragma nocover
1798
+
1799
+ # show named configurations
1800
+ if len(self._configs) > 0:
1801
+ table = ANSITable(
1802
+ Column("name", colalign=">"),
1803
+ *[
1804
+ Column(f"q{j:d}", colalign="<", headalign="<")
1805
+ for j in range(self.n)
1806
+ ],
1807
+ border=border,
1808
+ )
1809
+
1810
+ for name, q in self._configs.items():
1811
+ qlist = []
1812
+ for j, c in enumerate(self.structure):
1813
+ if c == "P":
1814
+ qlist.append(f"{q[j]: .3g}")
1815
+ else:
1816
+ qlist.append(angle(q[j], "{: .3g}"))
1817
+ table.row(name, *qlist)
1818
+
1819
+ return "\n" + str(table)
1820
+ else: # pragma nocover
1821
+ return ""
1822
+
1823
+ def random_q(self):
1824
+ """
1825
+ Return a random joint configuration
1826
+
1827
+ The value for each joint is uniform randomly distributed between the
1828
+ limits set for the robot.
1829
+
1830
+ .. rubric:: Notes
1831
+
1832
+ The joint limit for all joints must be set.
1833
+
1834
+ :returns: Random joint configuration :rtype: ndarray(n)
1835
+
1836
+ See Also
1837
+ --------
1838
+ :func:`Robot.qlim`
1839
+ :func:`Link.qlim`
1840
+
1841
+ """
1842
+
1843
+ qlim = self.qlim
1844
+ if np.any(np.isnan(qlim)):
1845
+ raise ValueError("some joint limits not defined") # pragma nocover
1846
+ return np.random.uniform(low=qlim[0, :], high=qlim[1, :], size=(self.n,))
1847
+
1848
+ def hierarchy(self) -> None:
1849
+ """
1850
+ Pretty print the robot link hierachy
1851
+
1852
+ :returns: Pretty print of the robot model
1853
+
1854
+ Examples
1855
+ --------
1856
+
1857
+ Makes a robot and prints the heirachy
1858
+
1859
+ .. runblock:: pycon
1860
+ >>> import roboticstoolbox as rtb
1861
+ >>> robot = rtb.models.URDF.Panda()
1862
+ >>> robot.hierarchy()
1863
+
1864
+ """
1865
+
1866
+ def recurse(link, indent=0):
1867
+ print(" " * indent * 2, link.name)
1868
+ if link.children is not None:
1869
+ for child in link.children:
1870
+ recurse(child, indent + 1)
1871
+
1872
+ recurse(self.base_link)
1873
+
1874
+ def segments(self) -> list[list[LinkType | None]]:
1875
+ """
1876
+ Segments of branched robot
1877
+
1878
+ For a single-chain robot with structure::
1879
+
1880
+ L1 - L2 - L3
1881
+
1882
+ the return is ``[[None, L1, L2, L3]]``
1883
+
1884
+ For a robot with structure::
1885
+
1886
+ L1 - L2 +- L3 - L4
1887
+ +- L5 - L6
1888
+
1889
+ the return is ``[[None, L1, L2], [L2, L3, L4], [L2, L5, L6]]``
1890
+
1891
+ :returns: Segment list
1892
+
1893
+ .. rubric:: Notes
1894
+
1895
+ - the length of the list is the number of segments in the robot
1896
+ - the first segment always starts with ``None`` which represents
1897
+ the base transform (since there is no base link)
1898
+ - the last link of one segment is also the first link of subsequent
1899
+ segments
1900
+ """
1901
+
1902
+ def recurse(link: Link):
1903
+ segs = [link.parent]
1904
+ while True:
1905
+ segs.append(link)
1906
+ if link.nchildren == 0:
1907
+ return [segs]
1908
+ elif link.nchildren == 1:
1909
+ link = link.children[0] # type: ignore
1910
+ continue
1911
+ elif link.nchildren > 1:
1912
+ segs = [segs]
1913
+
1914
+ for child in link.children: # type: ignore
1915
+ segs.extend(recurse(child))
1916
+
1917
+ return segs
1918
+
1919
+ return recurse(self.links[0]) # type: ignore
1920
+
1921
+ # --------------------------------------------------------------------- #
1922
+ # Scene Graph section
1923
+ # --------------------------------------------------------------------- #
1924
+
1925
+ def _update_link_tf(self, q: ArrayLike | None = None):
1926
+ """
1927
+ This private method updates the local transform of each link within
1928
+ this robot according to q (or self.q if q is none)
1929
+ """
1930
+
1931
+ @lru_cache(maxsize=2)
1932
+ def get_link_ets():
1933
+ return [link.ets._fknm for link in self.links]
1934
+
1935
+ @lru_cache(maxsize=2)
1936
+ def get_link_scene_node():
1937
+ return [link._T_reference for link in self.links]
1938
+
1939
+ Robot_link_T(get_link_ets(), get_link_scene_node(), self._q, q)
1940
+
1941
+ [gripper._update_link_tf() for gripper in self.grippers]
1942
+
1943
+ # --------------------------------------------------------------------- #
1944
+ # --------- PyPlot Methods -------------------------------------------- #
1945
+ # --------------------------------------------------------------------- #
1946
+
1947
+ def _get_graphical_backend(
1948
+ self,
1949
+ backend: L["swift", "pyplot", "pyplot2"] | None = None,
1950
+ ) -> Connector:
1951
+ import sys
1952
+ from roboticstoolbox.backends import load_backend
1953
+
1954
+ # Resolve which backend name to use
1955
+ if backend is not None:
1956
+ using_backend = backend.lower()
1957
+ else:
1958
+ # In JupyterLite/Pyodide, only pyplot/pyplot2 are available
1959
+ if sys.platform == "emscripten":
1960
+ if isinstance(self, rtb.Robot2):
1961
+ using_backend = "pyplot2"
1962
+ else:
1963
+ using_backend = "pyplot"
1964
+ else:
1965
+ # Infer from robot type and robot-level default override
1966
+ using_backend = self.default_backend
1967
+ if using_backend is None:
1968
+ if isinstance(self, rtb.DHRobot):
1969
+ using_backend = "pyplot"
1970
+ elif isinstance(self, rtb.Robot2):
1971
+ using_backend = "pyplot2"
1972
+ elif isinstance(self, rtb.Robot):
1973
+ using_backend = "swift" if self.hasgeometry else "pyplot"
1974
+ else:
1975
+ using_backend = "pyplot"
1976
+
1977
+ # swift is optional; fall back to pyplot with a helpful message
1978
+ if using_backend == "swift": # pragma nocover
1979
+ if isinstance(self, rtb.DHRobot):
1980
+ raise NotImplementedError(
1981
+ "Plotting in Swift is not implemented for DHRobots yet"
1982
+ )
1983
+ try:
1984
+ return load_backend("swift")
1985
+ except ModuleNotFoundError:
1986
+ if backend is not None:
1987
+ # user explicitly asked for swift
1988
+ print(
1989
+ "Swift is not installed. "
1990
+ "Install it with: pip install swift-sim"
1991
+ )
1992
+ using_backend = "pyplot"
1993
+
1994
+ return load_backend(using_backend)
1995
+
1996
+ def plot(
1997
+ self,
1998
+ q: ArrayLike,
1999
+ backend: L["swift", "pyplot", "pyplot2"] | None = None,
2000
+ block: bool = False,
2001
+ dt: float = 0.050,
2002
+ limits: ArrayLike | None = None,
2003
+ vellipse: bool = False,
2004
+ fellipse: bool = False,
2005
+ fig: str | None = None,
2006
+ movie: str | None = None,
2007
+ loop: bool = False,
2008
+ **kwargs,
2009
+ ) -> Connector:
2010
+ """
2011
+ Graphical display and animation
2012
+
2013
+ ``robot.plot(q, 'pyplot')`` displays a graphical view of a robot
2014
+ based on the kinematic model and the joint configuration ``q``.
2015
+ This is a stick figure polyline which joins the origins of the
2016
+ link coordinate frames. The plot will autoscale with an aspect
2017
+ ratio of 1.
2018
+
2019
+ If ``q`` (m,n) representing a joint-space trajectory it will create an
2020
+ animation with a pause of ``dt`` seconds between each frame.
2021
+
2022
+ :param q: The joint configuration of the robot.
2023
+ :param backend: The graphical backend to use, currently 'swift' and 'pyplot' are implemented. Defaults to 'swift' of a ``Robot`` and 'pyplot` for a ``DHRobot``
2024
+ :param block: Block operation of the code and keep the figure open
2025
+ :param dt: if q is a trajectory, this describes the delay in seconds between frames
2026
+ :param limits: Custom view limits for the plot. If not supplied will autoscale, [x1, x2, y1, y2, z1, z2] (this option is for 'pyplot' only)
2027
+ :param vellipse: (Plot Option) Plot the velocity ellipse at the end-effector (this option is for 'pyplot' only)
2028
+ :param fellipse: (Plot Option) Plot the force ellipse at the end-effector (this option is for 'pyplot' only)
2029
+ :param fig: (Plot Option) The figure label to plot in (this option is for 'pyplot' only)
2030
+ :param movie: (Plot Option) The filename to save the movie to (this option is for 'pyplot' only)
2031
+ :param loop: (Plot Option) Loop the movie (this option is for 'pyplot' only)
2032
+ :param jointaxes: (Plot Option) Plot an arrow indicating the axes in which the joint revolves around(revolute joint) or translates along (prosmatic joint) (this option is for 'pyplot' only)
2033
+ :param eeframe: (Plot Option) Plot the end-effector coordinate frame at the location of the end-effector. Uses three arrows, red, green and blue to indicate the x, y, and z-axes. (this option is for 'pyplot' only)
2034
+ :param shadow: (Plot Option) Plot a shadow of the robot in the x-y plane. (this option is for 'pyplot' only)
2035
+ :param name: (Plot Option) Plot the name of the robot near its base (this option is for 'pyplot' only)
2036
+ :param render_mode: (Plot Option) Rendering mode for matplotlib backends: ``'window'``, ``'notebook-widget'``, or ``'notebook-inline'``. If omitted, an environment-appropriate mode is selected.
2037
+ :param inline_every_n: (Plot Option) In notebook-inline mode, push one rendered frame every N simulation steps. Larger N reduces output load.
2038
+ :param inline_format: (Plot Option) In notebook-inline mode, frame format: ``'svg'`` (default) or ``'png'``.
2039
+ :param inline_dpi: (Plot Option) DPI for PNG inline frames only; ignored when ``inline_format='svg'``.
2040
+ :returns: A reference to the environment object which controls the figure
2041
+
2042
+ .. rubric:: Notes
2043
+
2044
+ - By default this method will block until the figure is dismissed.
2045
+ To avoid this set ``block=False``.
2046
+ - For PyPlot, the polyline joins the origins of the link frames,
2047
+ but for some Denavit-Hartenberg models those frames may not
2048
+ actually be on the robot, ie. the lines to not neccessarily
2049
+ represent the links of the robot.
2050
+
2051
+ See Also
2052
+ --------
2053
+ :func:`teach`
2054
+
2055
+ """
2056
+
2057
+ env = None
2058
+
2059
+ env = self._get_graphical_backend(backend)
2060
+
2061
+ launch_kwargs = {}
2062
+ for key in ("render_mode", "inline_every_n", "inline_format", "inline_dpi"):
2063
+ if key in kwargs:
2064
+ launch_kwargs[key] = kwargs.pop(key)
2065
+
2066
+ q = np.array(getmatrix(q, (None, self.n)))
2067
+ self.q = q[0, :]
2068
+
2069
+ # Add the self to the figure in readonly mode
2070
+ if q.shape[0] == 1:
2071
+ env.launch(
2072
+ name=self.name + " Plot", limits=limits, fig=fig, **launch_kwargs
2073
+ )
2074
+ else:
2075
+ env.launch(
2076
+ name=self.name + " Trajectory Plot",
2077
+ limits=limits,
2078
+ fig=fig,
2079
+ **launch_kwargs,
2080
+ )
2081
+
2082
+ env.add(self, readonly=True, **kwargs)
2083
+ self._active_plot_env = env
2084
+
2085
+ if vellipse:
2086
+ vell = self.vellipse(q[0], centre="ee", add=False)
2087
+ env.add(vell)
2088
+ else:
2089
+ vell = None
2090
+
2091
+ if fellipse:
2092
+ fell = self.fellipse(q[0], centre="ee", add=False)
2093
+ env.add(fell)
2094
+ else:
2095
+ fell = None
2096
+
2097
+ # List of images saved from each plot
2098
+ images = []
2099
+
2100
+ if movie is not None: # pragma: nocover
2101
+ loop = False
2102
+
2103
+ while True:
2104
+ for qk in q:
2105
+ self.q = qk
2106
+ if vell is not None:
2107
+ vell.q = qk
2108
+ if fell is not None:
2109
+ fell.q = qk
2110
+ env.step(dt)
2111
+
2112
+ if movie is not None and isinstance(env, PyPlot): # pragma nocover
2113
+ images.append(env.getframe())
2114
+
2115
+ if movie is not None: # pragma nocover
2116
+ # save it as an animated GIF
2117
+ images[0].save(
2118
+ movie,
2119
+ save_all=True,
2120
+ append_images=images[1:],
2121
+ optimize=False,
2122
+ duration=dt,
2123
+ loop=0,
2124
+ )
2125
+ if not loop:
2126
+ break
2127
+
2128
+ # Keep the plot open
2129
+ if block: # pragma nocover
2130
+ env.hold()
2131
+
2132
+ return env
2133
+
2134
+ def teach(
2135
+ self,
2136
+ q: ArrayLike | None,
2137
+ block: bool = True,
2138
+ limits: ArrayLike | None = None,
2139
+ vellipse: bool = False,
2140
+ fellipse: bool = False,
2141
+ backend: L["swift", "pyplot", "pyplot2"] | None = None,
2142
+ ) -> Connector:
2143
+ """
2144
+ Graphical teach pendant
2145
+
2146
+ ``robot.teach(q)`` opens a graphical view (PyPlot or Swift --
2147
+ see ``backend``) which allows the user to "drive" a graphical
2148
+ robot using a graphical slider panel. The robot's inital joint
2149
+ configuration is ``q``.
2150
+
2151
+ ``robot.teach()`` as above except the robot's stored value of ``q``
2152
+ is used.
2153
+
2154
+ :param q: The joint configuration of the robot (Optional,
2155
+ if not supplied will use the stored q values).
2156
+ :param block: Block operation of the code and keep the figure open
2157
+ :param limits: Custom view limits for the plot. If not supplied will
2158
+ autoscale, [x1, x2, y1, y2, z1, z2] (this option is for
2159
+ 'pyplot'/'pyplot2' only)
2160
+ :param vellipse: (Plot Option) Plot the velocity ellipse at the
2161
+ end-effector (this option is for 'pyplot' only)
2162
+ :param fellipse: (Plot Option) Plot the force ellipse at the
2163
+ end-effector (this option is for 'pyplot' only)
2164
+ :param backend: The graphical backend to use -- 'swift', 'pyplot',
2165
+ or 'pyplot2'. Defaults to whatever :meth:`plot` would pick for
2166
+ this robot (see its own ``backend`` for the resolution rule).
2167
+
2168
+ :returns: A reference to the environment object which controls
2169
+ the figure/view
2170
+
2171
+ .. rubric:: Notes
2172
+
2173
+ - Program execution is blocked until the teach window is
2174
+ dismissed. If ``block=False`` the method is non-blocking but
2175
+ you need to poll the window manager (PyPlot) or keep calling
2176
+ ``env.step()`` yourself (Swift) to ensure the window/panel
2177
+ remains responsive.
2178
+ - Once ``teach()`` returns (``block=True``), ``robot.q`` holds
2179
+ the final pose the sliders were left at -- true for every
2180
+ backend, even though Swift's own live joint state during the
2181
+ session lives on the returned environment's handle
2182
+ (``env.swift_objects[0].q``), not ``robot.q`` itself, to keep
2183
+ the robot model plain and shareable while teaching is
2184
+ in progress (see swift's ``AssemblyHandle``).
2185
+ - The slider limits are derived from the joint limit properties.
2186
+ If not set then:
2187
+
2188
+ - For revolute joints they are assumed to be [-pi, +pi]
2189
+ - For prismatic joint they are assumed unknown and an error
2190
+ occurs.
2191
+
2192
+ """
2193
+
2194
+ if q is None:
2195
+ q = np.zeros((self.n,))
2196
+ else:
2197
+ q = getvector(q, self.n)
2198
+
2199
+ # Make an empty 3D figure
2200
+ env = self._get_graphical_backend(backend)
2201
+
2202
+ if not getattr(env, "supports_teach", True): # pragma: nocover
2203
+ raise TypeError(
2204
+ f"teach() is not supported by the {type(env).__name__} backend"
2205
+ )
2206
+
2207
+ # Add the self to the figure in readonly mode
2208
+ env.launch(name="Teach " + self.name, limits=limits)
2209
+ handle = env.add(
2210
+ self,
2211
+ readonly=True,
2212
+ # jointaxes=jointaxes,
2213
+ # jointlabels=jointlabels,
2214
+ # eeframe=eeframe,
2215
+ # shadow=shadow,
2216
+ # name=name,
2217
+ )
2218
+ self._active_plot_env = env
2219
+
2220
+ # True if _add_teach_panel already fully handled block=True itself
2221
+ # (Swift's own env.run() loop -- see its docstring) -- PyPlot/
2222
+ # PyPlot2 never do (their own env.hold() below is what actually
2223
+ # enters matplotlib's blocking GUI mainloop), so their
2224
+ # _add_teach_panel implicitly returns None here, same as always.
2225
+ already_blocked = env._add_teach_panel(self, q, handle, block)
2226
+
2227
+ if vellipse:
2228
+ vell = self.vellipse(q, centre="ee", scale=0.5, add=False)
2229
+ env.add(vell)
2230
+
2231
+ if fellipse:
2232
+ fell = self.fellipse(q, centre="ee", add=False)
2233
+ env.add(fell)
2234
+
2235
+ # Keep the plot open -- skipped if the backend already blocked
2236
+ # itself above: calling this a second time isn't just wasteful,
2237
+ # it can hang outright for Swift (env.hold()'s disconnect-poll
2238
+ # never expires in headless mode, and even non-headless there's
2239
+ # a race where close() doesn't synchronously guarantee the
2240
+ # websocket's been dropped from socket.USERS before returning).
2241
+ if block and not already_blocked: # pragma nocover
2242
+ env.hold()
2243
+
2244
+ return env
2245
+
2246
+ # --------------------------------------------------------------------- #
2247
+
2248
+ # --------------------------------------------------------------------- #
2249
+ # --------- Utility Methods ------------------------------------------- #
2250
+ # --------------------------------------------------------------------- #
2251
+
2252
+ def showgraph(self, display_graph: bool = True, **kwargs) -> str | None:
2253
+ """
2254
+ Display a link transform graph in browser
2255
+
2256
+ ``robot.showgraph()`` displays a graph of the robot's link frames
2257
+ and the ETS between them. It uses GraphViz dot.
2258
+
2259
+ The nodes are:
2260
+ - Base is shown as a grey square. This is the world frame origin,
2261
+ but can be changed using the ``base`` attribute of the robot.
2262
+ - Link frames are indicated by circles
2263
+ - ETS transforms are indicated by rounded boxes
2264
+
2265
+ The edges are:
2266
+ - an arrow if `jtype` is False or the joint is fixed
2267
+ - an arrow with a round head if `jtype` is True and the joint is
2268
+ revolute
2269
+ - an arrow with a box head if `jtype` is True and the joint is
2270
+ prismatic
2271
+
2272
+ Edge labels or nodes in blue have a fixed transformation to the
2273
+ preceding link.
2274
+
2275
+ :param display_graph: Open the graph in a browser if True. Otherwise will return the file path
2276
+ :param etsbox: Put the link ETS in a box, otherwise an edge label
2277
+ :param jtype: Arrowhead to node indicates revolute or prismatic type
2278
+ :param static: Show static joints in blue and bold
2279
+
2280
+ Examples
2281
+ --------
2282
+
2283
+ >>> import roboticstoolbox as rtb
2284
+ >>> panda = rtb.models.URDF.Panda()
2285
+ >>> panda.showgraph()
2286
+
2287
+ .. image:: ../figs/panda-graph.svg
2288
+ :width: 600
2289
+
2290
+ See Also
2291
+ --------
2292
+ :func:`dotfile` :func:`graph`
2293
+
2294
+ """
2295
+
2296
+ # Lazy import
2297
+ import tempfile
2298
+ import subprocess
2299
+ import webbrowser
2300
+
2301
+ # create the temporary dotfile
2302
+ dotfile = tempfile.TemporaryFile(mode="w")
2303
+ self.dotfile(dotfile, **kwargs)
2304
+
2305
+ # rewind the dot file, create PDF file in the filesystem, run dot
2306
+ dotfile.seek(0)
2307
+ pdffile = tempfile.NamedTemporaryFile(suffix=".pdf", delete=False)
2308
+ subprocess.run("dot -Tpdf", shell=True, stdin=dotfile, stdout=pdffile)
2309
+
2310
+ # open the PDF file in browser (hopefully portable), then cleanup
2311
+ if display_graph: # pragma nocover
2312
+ webbrowser.open(f"file://{pdffile.name}")
2313
+ else:
2314
+ return pdffile.name
2315
+
2316
+ def dotfile(
2317
+ self,
2318
+ filename: str | IO[str],
2319
+ etsbox: bool = False,
2320
+ ets: L["full", "brief"] = "full",
2321
+ jtype: bool = False,
2322
+ static: bool = True,
2323
+ ):
2324
+ """
2325
+ Write a link transform graph as a GraphViz dot file
2326
+
2327
+ The file can be processed using dot::
2328
+
2329
+ % dot -Tpng -o out.png dotfile.dot
2330
+
2331
+ The nodes are:
2332
+
2333
+ - Base is shown as a grey square. This is the world frame origin,
2334
+ but can be changed using the ``base`` attribute of the robot.
2335
+ - Link frames are indicated by circles
2336
+ - ETS transforms are indicated by rounded boxes
2337
+
2338
+ The edges are:
2339
+
2340
+ - an arrow if `jtype` is False or the joint is fixed
2341
+ - an arrow with a round head if `jtype` is True and the joint is
2342
+ revolute
2343
+ - an arrow with a box head if `jtype` is True and the joint is
2344
+ prismatic
2345
+
2346
+ Edge labels or nodes in blue have a fixed transformation to the
2347
+ preceding link.
2348
+
2349
+ .. rubric:: Notes
2350
+
2351
+ If ``filename`` is a file object then the file will *not*
2352
+ be closed after the GraphViz model is written.
2353
+
2354
+ :param file: Name of file to write to
2355
+ :param etsbox: Put the link ETS in a box, otherwise an edge label
2356
+ :param ets: Display the full ets with "full" or a brief version with "brief"
2357
+ :param jtype: Arrowhead to node indicates revolute or prismatic type
2358
+ :param static: Show static joints in blue and bold
2359
+
2360
+ See Also
2361
+ --------
2362
+ :func:`showgraph` :func:`graph`
2363
+
2364
+ """
2365
+
2366
+ if isinstance(filename, str):
2367
+ file = open(filename, "w", encoding="utf-8")
2368
+ else:
2369
+ file = filename
2370
+
2371
+ header = r"""digraph G {
2372
+ graph [rankdir=LR];
2373
+ """
2374
+
2375
+ def draw_edge(link, etsbox, jtype, static):
2376
+ # draw the edge
2377
+ if jtype:
2378
+ if link.isprismatic:
2379
+ edge_options = 'arrowhead="box", arrowtail="inv", dir="both"'
2380
+ elif link.isrevolute:
2381
+ edge_options = 'arrowhead="dot", arrowtail="inv", dir="both"'
2382
+ else:
2383
+ edge_options = 'arrowhead="normal"'
2384
+ else:
2385
+ edge_options = 'arrowhead="normal"'
2386
+
2387
+ if link.parent is None:
2388
+ parent = "BASE"
2389
+ else:
2390
+ parent = link.parent.name
2391
+
2392
+ if etsbox:
2393
+ # put the ets fragment in a box
2394
+ if not link.isjoint and static:
2395
+ node_options = ', fontcolor="blue"'
2396
+ else:
2397
+ node_options = ""
2398
+
2399
+ try:
2400
+ file.write(
2401
+ ' {}_ets [shape=box, style=rounded, label="{}"{}];\n'.format(
2402
+ link.name,
2403
+ link.ets.__str__(q=f"q{link.jindex}"),
2404
+ node_options,
2405
+ )
2406
+ )
2407
+ except UnicodeEncodeError: # pragma nocover
2408
+ file.write(
2409
+ ' {}_ets [shape=box, style=rounded, label="{}"{}];\n'.format(
2410
+ link.name,
2411
+ link.ets.__str__(q=f"q{link.jindex}")
2412
+ .encode("ascii", "ignore")
2413
+ .decode("ascii"),
2414
+ node_options,
2415
+ )
2416
+ )
2417
+
2418
+ file.write(" {} -> {}_ets;\n".format(parent, link.name))
2419
+ file.write(
2420
+ " {}_ets -> {} [{}];\n".format(link.name, link.name, edge_options)
2421
+ )
2422
+ else:
2423
+ # put the ets fragment as an edge label
2424
+ if not link.isjoint and static:
2425
+ edge_options += ', fontcolor="blue"'
2426
+ if ets == "full":
2427
+ estr = link.ets.__str__(q=f"q{link.jindex}")
2428
+ elif ets == "brief":
2429
+ if link.jindex is None:
2430
+ estr = ""
2431
+ else:
2432
+ estr = f"...q{link.jindex}"
2433
+ else:
2434
+ return
2435
+ try:
2436
+ file.write(
2437
+ ' {} -> {} [label="{}", {}];\n'.format(
2438
+ parent,
2439
+ link.name,
2440
+ estr,
2441
+ edge_options,
2442
+ )
2443
+ )
2444
+ except UnicodeEncodeError: # pragma nocover
2445
+ file.write(
2446
+ ' {} -> {} [label="{}", {}];\n'.format(
2447
+ parent,
2448
+ link.name,
2449
+ estr.encode("ascii", "ignore").decode("ascii"),
2450
+ edge_options,
2451
+ )
2452
+ )
2453
+
2454
+ file.write(header)
2455
+
2456
+ # add the base link
2457
+ file.write(" BASE [shape=square, style=filled, fillcolor=gray]\n")
2458
+
2459
+ # add the links
2460
+ for link in self:
2461
+ # draw the link frame node (circle) or ee node (doublecircle)
2462
+ if link in self.ee_links:
2463
+ # end-effector
2464
+ node_options = 'shape="doublecircle", color="blue", fontcolor="blue"'
2465
+ else:
2466
+ node_options = 'shape="circle"'
2467
+
2468
+ file.write(" {} [{}];\n".format(link.name, node_options))
2469
+
2470
+ draw_edge(link, etsbox, jtype, static)
2471
+
2472
+ for gripper in self.grippers:
2473
+ for link in gripper.links:
2474
+ file.write(" {} [shape=cds];\n".format(link.name))
2475
+ draw_edge(link, etsbox, jtype, static)
2476
+
2477
+ file.write("}\n")
2478
+
2479
+ if isinstance(filename, str):
2480
+ file.close()
2481
+
2482
+ def graph(
2483
+ self,
2484
+ format: L["dot", "mermaid", "mermaid_fenced"] = "dot",
2485
+ filename: str | IO[str] | None = None,
2486
+ **kwargs,
2487
+ ) -> str:
2488
+ """
2489
+ Render a link transform graph as text
2490
+
2491
+ :param format: graph output format: ``"dot"``, ``"mermaid"`` or
2492
+ ``"mermaid_fenced"``, defaults to ``"dot"``
2493
+ :param filename: destination path or open text stream, defaults to
2494
+ None
2495
+ :param kwargs: for ``format="dot"``, forwarded to :meth:`dotfile`
2496
+ (``etsbox``, ``ets``, ``jtype``, ``static``); ignored otherwise
2497
+ :return: rendered graph text
2498
+
2499
+ Creates graph text representing the robot's link transform graph --
2500
+ the same structure :meth:`dotfile`/:meth:`showgraph` render, in a
2501
+ choice of formats. If ``filename`` is provided, the rendered text
2502
+ is also written to it.
2503
+
2504
+ ``"dot"`` produces the same GraphViz output as :meth:`dotfile`,
2505
+ just returned as a string rather than requiring a real file --
2506
+ ``etsbox``/``ets``/``jtype``/``static`` behave exactly as they do
2507
+ there.
2508
+
2509
+ ``"mermaid"``/``"mermaid_fenced"`` render a simplified `Mermaid
2510
+ <https://mermaid.js.org>`_ flowchart: one node per link (a double
2511
+ circle for end-effectors, a hexagon for gripper links), edges
2512
+ labelled with the joint type (``R``/``P``) and joint index for
2513
+ actuated joints. Mermaid has no equivalent of DOT's per-edge
2514
+ arrowhead/colour styling, so ``jtype``/``static``/``etsbox`` only
2515
+ affect ``"dot"`` output. ``"mermaid_fenced"`` wraps the same text
2516
+ in a fenced ``mermaid`` code block, ready to paste into Markdown
2517
+ or render directly in a Jupyter cell::
2518
+
2519
+ from IPython.display import Markdown, display
2520
+ display(Markdown(robot.graph(format="mermaid_fenced")))
2521
+
2522
+ :seealso: :meth:`dotfile` :meth:`showgraph`
2523
+ """
2524
+ graph_format = format.lower().replace("-", "_")
2525
+
2526
+ if graph_format == "dot":
2527
+ stream = io.StringIO()
2528
+ self.dotfile(stream, **kwargs)
2529
+ text = stream.getvalue()
2530
+
2531
+ elif graph_format in ("mermaid", "mermaid_fenced"):
2532
+ lines = ["flowchart LR"]
2533
+ node_ids: dict[Any, str] = {}
2534
+
2535
+ def node_id(link) -> str:
2536
+ return node_ids.setdefault(link, f"n{len(node_ids)}")
2537
+
2538
+ lines.append(' BASE["BASE"]')
2539
+
2540
+ for link in self:
2541
+ nid = node_id(link)
2542
+ if link in self.ee_links:
2543
+ lines.append(f' {nid}(("{link.name}"))')
2544
+ else:
2545
+ lines.append(f' {nid}["{link.name}"]')
2546
+
2547
+ for gripper in self.grippers:
2548
+ for link in gripper.links:
2549
+ lines.append(f' {node_id(link)}{{{{"{link.name}"}}}}')
2550
+
2551
+ def edge_label(link) -> str:
2552
+ if not link.isjoint:
2553
+ return ""
2554
+ kind = "P" if link.isprismatic else "R"
2555
+ return f"|{kind}{link.jindex}|"
2556
+
2557
+ for link in self:
2558
+ parent = "BASE" if link.parent is None else node_id(link.parent)
2559
+ lines.append(f" {parent} -->{edge_label(link)} {node_id(link)}")
2560
+
2561
+ for gripper in self.grippers:
2562
+ for link in gripper.links:
2563
+ parent = (
2564
+ "BASE" if link.parent is None else node_id(link.parent)
2565
+ )
2566
+ lines.append(
2567
+ f" {parent} -->{edge_label(link)} {node_id(link)}"
2568
+ )
2569
+
2570
+ mermaid_text = "\n".join(lines) + "\n"
2571
+ if graph_format == "mermaid_fenced":
2572
+ text = f"```mermaid\n{mermaid_text}```\n"
2573
+ else:
2574
+ text = mermaid_text
2575
+
2576
+ else:
2577
+ raise ValueError(
2578
+ f"unsupported graph format {format!r}, expected 'dot', "
2579
+ "'mermaid' or 'mermaid_fenced'"
2580
+ )
2581
+
2582
+ if filename is not None:
2583
+ if isinstance(filename, str):
2584
+ with open(filename, "w", encoding="utf-8") as f:
2585
+ f.write(text)
2586
+ else:
2587
+ filename.write(text)
2588
+
2589
+ return text