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