pydobotlab 0.1.0__tar.gz
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.
- pydobotlab-0.1.0/CONTRIBUTING.md +46 -0
- pydobotlab-0.1.0/LICENSE +21 -0
- pydobotlab-0.1.0/MANIFEST.in +14 -0
- pydobotlab-0.1.0/PKG-INFO +143 -0
- pydobotlab-0.1.0/README.md +105 -0
- pydobotlab-0.1.0/docs/README.md +54 -0
- pydobotlab-0.1.0/docs/SUMMARY.md +45 -0
- pydobotlab-0.1.0/docs/api/alarms.md +125 -0
- pydobotlab-0.1.0/docs/api/connection.md +91 -0
- pydobotlab-0.1.0/docs/api/discovery.md +159 -0
- pydobotlab-0.1.0/docs/api/end-effectors.md +84 -0
- pydobotlab-0.1.0/docs/api/errors.md +126 -0
- pydobotlab-0.1.0/docs/api/extras.md +101 -0
- pydobotlab-0.1.0/docs/api/io.md +128 -0
- pydobotlab-0.1.0/docs/api/jog.md +90 -0
- pydobotlab-0.1.0/docs/api/motion.md +207 -0
- pydobotlab-0.1.0/docs/api/overview.md +62 -0
- pydobotlab-0.1.0/docs/api/pose-stream.md +68 -0
- pydobotlab-0.1.0/docs/api/queue.md +128 -0
- pydobotlab-0.1.0/docs/api/speed.md +123 -0
- pydobotlab-0.1.0/docs/getting-started/connect.md +91 -0
- pydobotlab-0.1.0/docs/getting-started/first-script.md +72 -0
- pydobotlab-0.1.0/docs/getting-started/install.md +65 -0
- pydobotlab-0.1.0/docs/getting-started/linux.md +219 -0
- pydobotlab-0.1.0/docs/getting-started/simulator.md +68 -0
- pydobotlab-0.1.0/docs/panel/broker.md +75 -0
- pydobotlab-0.1.0/docs/panel/overview.md +52 -0
- pydobotlab-0.1.0/docs/panel/running.md +88 -0
- pydobotlab-0.1.0/docs/protocol/alarms.md +62 -0
- pydobotlab-0.1.0/docs/protocol/command-ids.md +186 -0
- pydobotlab-0.1.0/docs/protocol/ctrl-byte.md +62 -0
- pydobotlab-0.1.0/docs/protocol/frame-format.md +96 -0
- pydobotlab-0.1.0/docs/protocol/method-to-cmd.md +74 -0
- pydobotlab-0.1.0/docs/publishing.md +106 -0
- pydobotlab-0.1.0/examples/draw_smiley.py +434 -0
- pydobotlab-0.1.0/examples/multi_dobot.py +73 -0
- pydobotlab-0.1.0/examples/pick_and_place.py +59 -0
- pydobotlab-0.1.0/examples/smoke_test.py +114 -0
- pydobotlab-0.1.0/examples/speed_test.py +78 -0
- pydobotlab-0.1.0/examples/two_dobots_with_panels.py +95 -0
- pydobotlab-0.1.0/examples/unreachable_target.py +66 -0
- pydobotlab-0.1.0/mkdocs.yml +88 -0
- pydobotlab-0.1.0/pydobotlab/__init__.py +70 -0
- pydobotlab-0.1.0/pydobotlab/alarms.py +240 -0
- pydobotlab-0.1.0/pydobotlab/broker.py +355 -0
- pydobotlab-0.1.0/pydobotlab/broker_protocol.py +35 -0
- pydobotlab-0.1.0/pydobotlab/commands.py +126 -0
- pydobotlab-0.1.0/pydobotlab/device.py +1021 -0
- pydobotlab-0.1.0/pydobotlab/discovery.py +299 -0
- pydobotlab-0.1.0/pydobotlab/errors.py +57 -0
- pydobotlab-0.1.0/pydobotlab/panel/__init__.py +9 -0
- pydobotlab-0.1.0/pydobotlab/panel/__main__.py +10 -0
- pydobotlab-0.1.0/pydobotlab/panel/app.py +120 -0
- pydobotlab-0.1.0/pydobotlab/panel/hub.py +204 -0
- pydobotlab-0.1.0/pydobotlab/panel/panel.py +648 -0
- pydobotlab-0.1.0/pydobotlab/panel/style.qss +224 -0
- pydobotlab-0.1.0/pydobotlab/panel/widgets.py +206 -0
- pydobotlab-0.1.0/pydobotlab/protocol.py +248 -0
- pydobotlab-0.1.0/pydobotlab/queue.py +63 -0
- pydobotlab-0.1.0/pydobotlab/simulator.py +650 -0
- pydobotlab-0.1.0/pydobotlab/transport.py +303 -0
- pydobotlab-0.1.0/pydobotlab.egg-info/PKG-INFO +143 -0
- pydobotlab-0.1.0/pydobotlab.egg-info/SOURCES.txt +77 -0
- pydobotlab-0.1.0/pydobotlab.egg-info/dependency_links.txt +1 -0
- pydobotlab-0.1.0/pydobotlab.egg-info/entry_points.txt +2 -0
- pydobotlab-0.1.0/pydobotlab.egg-info/requires.txt +14 -0
- pydobotlab-0.1.0/pydobotlab.egg-info/top_level.txt +1 -0
- pydobotlab-0.1.0/pyproject.toml +81 -0
- pydobotlab-0.1.0/scripts/check_installed_package.py +25 -0
- pydobotlab-0.1.0/setup.cfg +4 -0
- pydobotlab-0.1.0/tests/__init__.py +0 -0
- pydobotlab-0.1.0/tests/test_alignment.py +182 -0
- pydobotlab-0.1.0/tests/test_broker.py +157 -0
- pydobotlab-0.1.0/tests/test_panel_imports.py +51 -0
- pydobotlab-0.1.0/tests/test_panel_widgets.py +82 -0
- pydobotlab-0.1.0/tests/test_protocol.py +188 -0
- pydobotlab-0.1.0/tests/test_registry.py +195 -0
- pydobotlab-0.1.0/tests/test_simulator.py +174 -0
- pydobotlab-0.1.0/tests/test_transport_regressions.py +136 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Use Python 3.10 or newer and a virtual environment:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python -m venv .venv
|
|
7
|
+
source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
|
|
8
|
+
python -m pip install -e ".[dev,gui]"
|
|
9
|
+
python -m pytest --timeout=30
|
|
10
|
+
ruff check .
|
|
11
|
+
ruff format --check .
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Tests use simulated devices; they do not move connected hardware. The GUI
|
|
15
|
+
can be exercised without a display with `QT_QPA_PLATFORM=offscreen`.
|
|
16
|
+
|
|
17
|
+
## Python style
|
|
18
|
+
|
|
19
|
+
- Use descriptive verbs for functions and descriptive nouns for variables.
|
|
20
|
+
Keep established coordinate names (`x`, `y`, `z`, `r`) and the documented
|
|
21
|
+
DobotLab method signatures so existing scripts remain compatible.
|
|
22
|
+
- Prefer direct code to trivial forwarding helpers. Use public methods for
|
|
23
|
+
supported operations and GUI callbacks; reserve leading underscores for
|
|
24
|
+
state and implementation details that require internal synchronization.
|
|
25
|
+
- Use `pathlib.Path` for filesystem paths, explicit UTF-8 for text files,
|
|
26
|
+
built-in generic types, and `Type | None` for optional values.
|
|
27
|
+
- Keep imports at module scope unless an optional dependency or circular
|
|
28
|
+
dependency requires delaying them. Do not use dynamic `__import__` calls.
|
|
29
|
+
- Run Ruff and the hardware-free tests before submitting changes. Add
|
|
30
|
+
regression tests when fixing behavior, especially transport and queue bugs.
|
|
31
|
+
|
|
32
|
+
## Package boundaries
|
|
33
|
+
|
|
34
|
+
`protocol.py` encodes frames, `transport.py` owns serial/TCP connections,
|
|
35
|
+
`device.py` exposes the robot API, and `discovery.py` finds devices. The
|
|
36
|
+
optional `panel` package contains the Qt GUI. Keep Qt imports out of the
|
|
37
|
+
base library. `simulator.py` supplies a software serial backend for testing.
|
|
38
|
+
|
|
39
|
+
The release process is described in [docs/publishing.md](docs/publishing.md).
|
|
40
|
+
|
|
41
|
+
## Documentation
|
|
42
|
+
|
|
43
|
+
Install `python -m pip install -e ".[docs]"`, then use `mkdocs serve` to
|
|
44
|
+
preview or `mkdocs build --strict` to check links and build the site.
|
|
45
|
+
Navigation is configured in `mkdocs.yml`; retain `docs/SUMMARY.md` as the
|
|
46
|
+
Markdown table of contents.
|
pydobotlab-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tal Eylon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
include LICENSE README.md CONTRIBUTING.md
|
|
2
|
+
recursive-include pydobotlab *.py *.qss
|
|
3
|
+
recursive-include tests *.py
|
|
4
|
+
recursive-include examples *.py
|
|
5
|
+
recursive-include docs *.md
|
|
6
|
+
recursive-include scripts *.py
|
|
7
|
+
prune .github
|
|
8
|
+
prune .venv
|
|
9
|
+
prune build
|
|
10
|
+
prune dist
|
|
11
|
+
global-exclude __pycache__ *.py[cod]
|
|
12
|
+
include mkdocs.yml
|
|
13
|
+
prune site
|
|
14
|
+
global-exclude PyPI-Recovery-Codes-*.txt
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pydobotlab
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pure-Python control library for the Dobot Magician robot arm. A lightweight, cross-platform replacement for DobotStudio / DobotLab. Tel Aviv University CIM Lab
|
|
5
|
+
Author-email: Tal Eylon <taleylon1@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://taleylon.github.io/pydobotlab/
|
|
8
|
+
Project-URL: Documentation, https://taleylon.github.io/pydobotlab/
|
|
9
|
+
Project-URL: Repository, https://github.com/taleylon/pydobotlab
|
|
10
|
+
Project-URL: Issues, https://github.com/taleylon/pydobotlab/issues
|
|
11
|
+
Keywords: dobot,magician,robotics,serial,education
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Education
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: pyserial>=3.5
|
|
27
|
+
Provides-Extra: gui
|
|
28
|
+
Requires-Dist: PySide6>=6.5; extra == "gui"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-timeout>=2.3; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
33
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=6.1; extra == "dev"
|
|
35
|
+
Provides-Extra: docs
|
|
36
|
+
Requires-Dist: mkdocs-material<10,>=9.6; extra == "docs"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# pydobotlab
|
|
40
|
+
|
|
41
|
+
Python control for the **Dobot Magician** robot arm, with an optional desktop
|
|
42
|
+
control panel and a simulator for working without hardware. Communicates
|
|
43
|
+
over serial using `pyserial`; no vendor DLL or DobotStudio installation is required.
|
|
44
|
+
|
|
45
|
+
**[Documentation & homepage](https://taleylon.github.io/pydobotlab/)** ·
|
|
46
|
+
[API reference](https://taleylon.github.io/pydobotlab/api/overview/) ·
|
|
47
|
+
[GitHub](https://github.com/taleylon/pydobotlab) ·
|
|
48
|
+
[Report an issue](https://github.com/taleylon/pydobotlab/issues)
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
Requires **Python 3.10 or newer**. Install in a virtual environment:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
python -m pip install pydobotlab
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For the optional PySide6 desktop panel:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
python -m pip install "pydobotlab[gui]"
|
|
62
|
+
pydobotlab-panel
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The base library requires only `pyserial`; Qt is installed only with the
|
|
66
|
+
`gui` extra. See the [installation guide](https://taleylon.github.io/pydobotlab/getting-started/install/)
|
|
67
|
+
for virtual environments, Windows drivers, and Linux serial-port permissions.
|
|
68
|
+
The project has been used on Ubuntu and Windows; macOS hardware has not been verified.
|
|
69
|
+
|
|
70
|
+
## Quick start
|
|
71
|
+
|
|
72
|
+
With the arm connected and its workspace clear:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from pydobotlab import Magician, PTPMode
|
|
76
|
+
|
|
77
|
+
with Magician() as robot: # auto-discover; or specify "/dev/ttyUSB0" / "COM3"
|
|
78
|
+
robot.clear_alarm()
|
|
79
|
+
robot.set_home()
|
|
80
|
+
robot.move_to(220, 0, 50, mode=PTPMode.MOVJ_XYZ)
|
|
81
|
+
pose = robot.get_pose()
|
|
82
|
+
print(pose.x, pose.y, pose.z, pose.joints)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`Dobot` is also available as an alias for `Magician`. The documented DobotLab
|
|
86
|
+
method names and parameters are preserved for existing scripts and teaching material.
|
|
87
|
+
|
|
88
|
+
## Try it without hardware
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pydobotlab-panel --simulator
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Or use the simulator from Python:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from pydobotlab import Magician
|
|
98
|
+
from pydobotlab.simulator import install_simulator, stop_all
|
|
99
|
+
|
|
100
|
+
install_simulator(arms=1)
|
|
101
|
+
try:
|
|
102
|
+
with Magician("/dev/sim0", via_broker=False) as robot:
|
|
103
|
+
robot.move_to(220, 0, 50)
|
|
104
|
+
print(robot.get_pose())
|
|
105
|
+
finally:
|
|
106
|
+
stop_all()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The simulator supports offline examples and tests. It approximates motion
|
|
110
|
+
and firmware behavior; it does not validate a real robot's trajectory.
|
|
111
|
+
|
|
112
|
+
## Features and examples
|
|
113
|
+
|
|
114
|
+
- Cartesian and joint motion, jogging, homing, speed settings, and continuous paths.
|
|
115
|
+
- Suction cup, gripper, laser, digital I/O, sensors, and conveyor commands.
|
|
116
|
+
- Structured alarms, motion-failure exceptions, and live pose streaming.
|
|
117
|
+
- Firmware queue control and `with robot.batch()` for accumulating commands.
|
|
118
|
+
- Multiple robot connections and a local broker for sharing a port between the GUI and scripts.
|
|
119
|
+
|
|
120
|
+
Start with [pick and place](https://github.com/taleylon/pydobotlab/blob/main/examples/pick_and_place.py),
|
|
121
|
+
[smiley drawing](https://github.com/taleylon/pydobotlab/blob/main/examples/draw_smiley.py),
|
|
122
|
+
or [two arms with live panels](https://github.com/taleylon/pydobotlab/blob/main/examples/two_dobots_with_panels.py).
|
|
123
|
+
The [user guide](https://taleylon.github.io/pydobotlab/) covers the API,
|
|
124
|
+
control panel, broker, and wire protocol.
|
|
125
|
+
|
|
126
|
+
## Development
|
|
127
|
+
|
|
128
|
+
From a local checkout, including before the first PyPI release:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
python -m pip install -e ".[dev,gui,docs]"
|
|
132
|
+
python -m pytest --timeout=30
|
|
133
|
+
ruff check .
|
|
134
|
+
ruff format --check .
|
|
135
|
+
mkdocs serve
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
See [contributor guidance](https://github.com/taleylon/pydobotlab/blob/main/CONTRIBUTING.md)
|
|
139
|
+
and the [publishing guide](https://taleylon.github.io/pydobotlab/publishing/).
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT. Maintained by Tal Eylon, <taleylon1@gmail.com>.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# pydobotlab
|
|
2
|
+
|
|
3
|
+
Python control for the **Dobot Magician** robot arm, with an optional desktop
|
|
4
|
+
control panel and a simulator for working without hardware. Communicates
|
|
5
|
+
over serial using `pyserial`; no vendor DLL or DobotStudio installation is required.
|
|
6
|
+
|
|
7
|
+
**[Documentation & homepage](https://taleylon.github.io/pydobotlab/)** ·
|
|
8
|
+
[API reference](https://taleylon.github.io/pydobotlab/api/overview/) ·
|
|
9
|
+
[GitHub](https://github.com/taleylon/pydobotlab) ·
|
|
10
|
+
[Report an issue](https://github.com/taleylon/pydobotlab/issues)
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
Requires **Python 3.10 or newer**. Install in a virtual environment:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
python -m pip install pydobotlab
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For the optional PySide6 desktop panel:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
python -m pip install "pydobotlab[gui]"
|
|
24
|
+
pydobotlab-panel
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The base library requires only `pyserial`; Qt is installed only with the
|
|
28
|
+
`gui` extra. See the [installation guide](https://taleylon.github.io/pydobotlab/getting-started/install/)
|
|
29
|
+
for virtual environments, Windows drivers, and Linux serial-port permissions.
|
|
30
|
+
The project has been used on Ubuntu and Windows; macOS hardware has not been verified.
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
With the arm connected and its workspace clear:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from pydobotlab import Magician, PTPMode
|
|
38
|
+
|
|
39
|
+
with Magician() as robot: # auto-discover; or specify "/dev/ttyUSB0" / "COM3"
|
|
40
|
+
robot.clear_alarm()
|
|
41
|
+
robot.set_home()
|
|
42
|
+
robot.move_to(220, 0, 50, mode=PTPMode.MOVJ_XYZ)
|
|
43
|
+
pose = robot.get_pose()
|
|
44
|
+
print(pose.x, pose.y, pose.z, pose.joints)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`Dobot` is also available as an alias for `Magician`. The documented DobotLab
|
|
48
|
+
method names and parameters are preserved for existing scripts and teaching material.
|
|
49
|
+
|
|
50
|
+
## Try it without hardware
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pydobotlab-panel --simulator
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or use the simulator from Python:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from pydobotlab import Magician
|
|
60
|
+
from pydobotlab.simulator import install_simulator, stop_all
|
|
61
|
+
|
|
62
|
+
install_simulator(arms=1)
|
|
63
|
+
try:
|
|
64
|
+
with Magician("/dev/sim0", via_broker=False) as robot:
|
|
65
|
+
robot.move_to(220, 0, 50)
|
|
66
|
+
print(robot.get_pose())
|
|
67
|
+
finally:
|
|
68
|
+
stop_all()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The simulator supports offline examples and tests. It approximates motion
|
|
72
|
+
and firmware behavior; it does not validate a real robot's trajectory.
|
|
73
|
+
|
|
74
|
+
## Features and examples
|
|
75
|
+
|
|
76
|
+
- Cartesian and joint motion, jogging, homing, speed settings, and continuous paths.
|
|
77
|
+
- Suction cup, gripper, laser, digital I/O, sensors, and conveyor commands.
|
|
78
|
+
- Structured alarms, motion-failure exceptions, and live pose streaming.
|
|
79
|
+
- Firmware queue control and `with robot.batch()` for accumulating commands.
|
|
80
|
+
- Multiple robot connections and a local broker for sharing a port between the GUI and scripts.
|
|
81
|
+
|
|
82
|
+
Start with [pick and place](https://github.com/taleylon/pydobotlab/blob/main/examples/pick_and_place.py),
|
|
83
|
+
[smiley drawing](https://github.com/taleylon/pydobotlab/blob/main/examples/draw_smiley.py),
|
|
84
|
+
or [two arms with live panels](https://github.com/taleylon/pydobotlab/blob/main/examples/two_dobots_with_panels.py).
|
|
85
|
+
The [user guide](https://taleylon.github.io/pydobotlab/) covers the API,
|
|
86
|
+
control panel, broker, and wire protocol.
|
|
87
|
+
|
|
88
|
+
## Development
|
|
89
|
+
|
|
90
|
+
From a local checkout, including before the first PyPI release:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
python -m pip install -e ".[dev,gui,docs]"
|
|
94
|
+
python -m pytest --timeout=30
|
|
95
|
+
ruff check .
|
|
96
|
+
ruff format --check .
|
|
97
|
+
mkdocs serve
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
See [contributor guidance](https://github.com/taleylon/pydobotlab/blob/main/CONTRIBUTING.md)
|
|
101
|
+
and the [publishing guide](https://taleylon.github.io/pydobotlab/publishing/).
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
MIT. Maintained by Tal Eylon, <taleylon1@gmail.com>.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# pydobotlab — User Guide
|
|
2
|
+
|
|
3
|
+
`pydobotlab` is a pure-Python control library and GUI for the **Dobot Magician** robot arm. It replaces the official DobotStudio / DobotLab desktop app with something students can drive from a Python script *and* a control panel at the same time.
|
|
4
|
+
|
|
5
|
+
Browse the guide using the sidebar, search for an API method, or use the [table of contents](SUMMARY.md). The Markdown source lives in the [GitHub repository](https://github.com/taleylon/pydobotlab/tree/main/docs). If you're new, start with [Getting Started](getting-started/install.md). If you already know the SDK and just want to look up a command, jump to the [API Reference](api/overview.md).
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python -m pip install pydobotlab
|
|
9
|
+
# Optional desktop control panel:
|
|
10
|
+
python -m pip install "pydobotlab[gui]"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
[Get started](getting-started/install.md){ .md-button .md-button--primary }
|
|
14
|
+
[View on PyPI](https://pypi.org/project/pydobotlab/){ .md-button }
|
|
15
|
+
|
|
16
|
+
## What's in this guide
|
|
17
|
+
|
|
18
|
+
The guide is split into four parts:
|
|
19
|
+
|
|
20
|
+
1. **[Getting Started](getting-started/install.md)** — installing the package, connecting to an arm, running your first program, and the simulator.
|
|
21
|
+
2. **[API Reference](api/overview.md)** — every public function on `Magician`, grouped by what it does (motion, alarms, queue, end-effectors, I/O, ...). Each entry says what it does, what it returns, and which Dobot protocol command it sends.
|
|
22
|
+
3. **[Control Panel & Broker](panel/overview.md)** — the PySide6 GUI, the multi-arm hub, and the TCP broker that lets the panel and a script share one arm.
|
|
23
|
+
4. **[Protocol Reference](protocol/frame-format.md)** — the wire-level Dobot protocol: frame format, control byte, the full `CommandID` table, and how each ID maps back to a `Magician` method.
|
|
24
|
+
|
|
25
|
+
## How the layers fit together
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
┌───────────────────────────────┐ ┌────────────────────────────┐
|
|
29
|
+
│ your script (Magician API) │ │ ControlPanel (PySide6) │
|
|
30
|
+
└───────────────┬───────────────┘ └──────────────┬─────────────┘
|
|
31
|
+
│ │
|
|
32
|
+
└──────────────┬────────────────────┘
|
|
33
|
+
▼
|
|
34
|
+
┌─────────────────────┐
|
|
35
|
+
│ DobotBroker │ TCP 127.0.0.1:8765
|
|
36
|
+
│ (per-port refcnt) │ (auto-spawned by panel)
|
|
37
|
+
└──────────┬──────────┘
|
|
38
|
+
▼
|
|
39
|
+
┌─────────────────────┐
|
|
40
|
+
│ SerialTransport │ 115200 8N1
|
|
41
|
+
└──────────┬──────────┘
|
|
42
|
+
▼
|
|
43
|
+
┌─────────────────────┐
|
|
44
|
+
│ Dobot V1.1.5 wire │ AA AA len id ctrl … cks
|
|
45
|
+
└─────────────────────┘
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The script-side and panel-side both speak through the same `Magician` class. When the panel is open the broker mediates so both can drive one arm; when there's no panel, `Magician` opens the serial port directly.
|
|
49
|
+
|
|
50
|
+
## Quick links
|
|
51
|
+
|
|
52
|
+
- Source code on disk: `pydobotlab/` (library), `pydobotlab/panel/` (GUI), `examples/`.
|
|
53
|
+
- Protocol PDF reference: [Dobot Communication Protocol V1.1.5](https://www.alcom.no/wp-content/uploads/2019/11/Dobot-Communication-Protocol-V1.1.5-1.pdf) — the canonical source for command IDs and parameter layouts. Every page in the [Protocol Reference](protocol/frame-format.md) section cites it.
|
|
54
|
+
- Magician feature page: [Dobot Magician — features](https://www.dobot-robots.com/products/education/magician.html).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Summary
|
|
2
|
+
|
|
3
|
+
* [Introduction](README.md)
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
* [Installation](getting-started/install.md)
|
|
8
|
+
* [Connecting to an arm](getting-started/connect.md)
|
|
9
|
+
* [Hello, Magician](getting-started/first-script.md)
|
|
10
|
+
* [Running without hardware (simulator)](getting-started/simulator.md)
|
|
11
|
+
* [Linux setup (Ubuntu / Debian)](getting-started/linux.md)
|
|
12
|
+
|
|
13
|
+
## API Reference
|
|
14
|
+
|
|
15
|
+
* [Overview](api/overview.md)
|
|
16
|
+
* [Connection lifecycle](api/connection.md)
|
|
17
|
+
* [Pose & motion](api/motion.md)
|
|
18
|
+
* [JOG (live, non-trajectory motion)](api/jog.md)
|
|
19
|
+
* [Speed & motion parameters](api/speed.md)
|
|
20
|
+
* [Alarms](api/alarms.md)
|
|
21
|
+
* [Queue control](api/queue.md)
|
|
22
|
+
* [End-effectors](api/end-effectors.md)
|
|
23
|
+
* [Digital I/O & sensors](api/io.md)
|
|
24
|
+
* [Conveyor & extras](api/extras.md)
|
|
25
|
+
* [Real-time pose stream](api/pose-stream.md)
|
|
26
|
+
* [Discovery (finding arms)](api/discovery.md)
|
|
27
|
+
* [Errors & exceptions](api/errors.md)
|
|
28
|
+
|
|
29
|
+
## Control Panel & Broker
|
|
30
|
+
|
|
31
|
+
* [Overview](panel/overview.md)
|
|
32
|
+
* [Running the panel](panel/running.md)
|
|
33
|
+
* [The DobotBroker (multi-client)](panel/broker.md)
|
|
34
|
+
|
|
35
|
+
## Protocol Reference
|
|
36
|
+
|
|
37
|
+
* [Frame format](protocol/frame-format.md)
|
|
38
|
+
* [Control byte (rw / isQueued)](protocol/ctrl-byte.md)
|
|
39
|
+
* [Command ID table](protocol/command-ids.md)
|
|
40
|
+
* [Method ↔ command mapping](protocol/method-to-cmd.md)
|
|
41
|
+
* [Alarm bitmask](protocol/alarms.md)
|
|
42
|
+
|
|
43
|
+
## Project
|
|
44
|
+
|
|
45
|
+
* [Publishing and maintaining the project](publishing.md)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Alarms
|
|
2
|
+
|
|
3
|
+
The arm reports faults as a 16-byte (128-bit) bitmask. Each bit is a named alarm code (`0x00..0x7F`); `byte_index = code // 8`, `bit_index = code % 8`. pydobotlab decodes that mask into a friendly [`AlarmSet`](#alarmset) with names *and* per-alarm troubleshoot lines.
|
|
4
|
+
|
|
5
|
+
When motion alarms fire, pydobotlab surfaces them as [`DobotKinematicError`](errors.md#dobotkinematicerror-extends-dobotalarmerror) from the waiters in [`move_to`](motion.md#move-to) / [`set_home`](motion.md#set_home-waittrue-timeout600-raise_on_alarmtrue-int) / [`wait_for`](queue.md#wait_forindex-timeoutnone-poll002-raise_on_alarmtrue-none) / [`wait_idle`](queue.md#wait_idle-timeoutnone-poll002-raise_on_alarmtrue-none), so you don't sit on a `wait_for` forever for a command the firmware has refused.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## `get_alarms() -> AlarmSet`
|
|
10
|
+
|
|
11
|
+
**Purpose.** Read and decode the firmware's 16-byte alarm bitmask.
|
|
12
|
+
|
|
13
|
+
**Inputs.** None.
|
|
14
|
+
|
|
15
|
+
**Returns.** [`AlarmSet`](#alarmset).
|
|
16
|
+
|
|
17
|
+
**Protocol.** [`GET_ALARMS_STATE`](../protocol/command-ids.md) (20), read + immediate. Response: 16 raw bytes (the mask).
|
|
18
|
+
|
|
19
|
+
**Example.**
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
alarms = bot.get_alarms()
|
|
23
|
+
if alarms:
|
|
24
|
+
for line in alarms.format():
|
|
25
|
+
print(" •", line)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## `clear_alarm(*, verify=False, settle=0.15) -> None` {#clear-alarm}
|
|
31
|
+
|
|
32
|
+
**Purpose.** Clear all active alarms.
|
|
33
|
+
|
|
34
|
+
**Inputs.**
|
|
35
|
+
|
|
36
|
+
| Arg | Type | Default | Meaning |
|
|
37
|
+
|-----|------|---------|---------|
|
|
38
|
+
| `verify` | `bool` | `False` | After clearing, sleep `settle` seconds and re-poll. If any *motion-related* alarm bit is still set, the firmware honoured the clear but the **physical condition** that triggered it (e.g. a joint mashed against a limit) is still active — raise [`DobotAlarmError`](errors.md#dobotalarmerror) so the student knows to fix the physical state, not just the bitmask. |
|
|
39
|
+
| `settle` | `float` | `0.15` | Verification settle delay in seconds. Only relevant when `verify=True`. |
|
|
40
|
+
|
|
41
|
+
**Returns.** `None`.
|
|
42
|
+
|
|
43
|
+
**Raises.** [`DobotAlarmError`](errors.md#dobotalarmerror) when `verify=True` and an alarm reasserts itself.
|
|
44
|
+
|
|
45
|
+
**Protocol.** [`CLEAR_ALL_ALARMS_STATE`](../protocol/command-ids.md) (20), **write** + immediate. Param: empty. Response: ack.
|
|
46
|
+
|
|
47
|
+
> **Note** — the protocol uses the same command ID (`20`) for read (get) and write (clear); the [`rw` bit](../protocol/ctrl-byte.md) of the control byte is what distinguishes them.
|
|
48
|
+
|
|
49
|
+
**Example — verify-on-clear.**
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from pydobotlab.errors import DobotAlarmError
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
bot.clear_alarm(verify=True)
|
|
56
|
+
except DobotAlarmError as e:
|
|
57
|
+
print("alarm reasserted — fix the physical condition first:")
|
|
58
|
+
for name in e.alarms:
|
|
59
|
+
print(" •", name)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`clear_alarms()` (plural) is kept as a deprecated alias.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## `ensure_no_alarms() -> None`
|
|
67
|
+
|
|
68
|
+
**Purpose.** Guard call — raise [`DobotAlarmError`](errors.md#dobotalarmerror) if any alarm is currently active.
|
|
69
|
+
|
|
70
|
+
**Returns.** `None`.
|
|
71
|
+
|
|
72
|
+
**Protocol.** Sends `GET_ALARMS_STATE` (20) and inspects the result.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## `AlarmSet`
|
|
77
|
+
|
|
78
|
+
`from pydobotlab import AlarmSet`. The decoded result of `get_alarms()`.
|
|
79
|
+
|
|
80
|
+
| Attribute / Method | Returns | What |
|
|
81
|
+
|-------------------------------|---------|------|
|
|
82
|
+
| `raw` | `bytes` | The original 16-byte mask, untouched. |
|
|
83
|
+
| `alarms` | `tuple[Alarm \| int, ...]` | Active alarm codes, named when known. |
|
|
84
|
+
| `bool(alarmset)` | `bool` | `True` iff any bit is set. |
|
|
85
|
+
| `iter(alarmset)` | iter | Iterates the active alarm codes. |
|
|
86
|
+
| `name in alarmset` | `bool` | Membership test. |
|
|
87
|
+
| `.names()` | `list[str]` | Friendly names, including categorised labels for unmapped codes (e.g. `JOINT_LIMIT_POS_0x44`). |
|
|
88
|
+
| `.format()` | `list[str]` | One `"{TAG}: {explanation + how to fix}"` line per active alarm — what the panel banner and `DobotKinematicError.message` use. |
|
|
89
|
+
| `.describe()` | `list[str]` | Backwards-compat alias for `.format()`. |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## `Alarm` codes
|
|
94
|
+
|
|
95
|
+
`from pydobotlab import Alarm`. The named bit positions in the mask. Categorised by byte:
|
|
96
|
+
|
|
97
|
+
| Byte | Range | Members |
|
|
98
|
+
|------|-------|---------|
|
|
99
|
+
| 0 | `0x00..0x07` | `PUBLIC_RESET`, `PUBLIC_UNDEFINED_INSTRUCTION`, `PUBLIC_FILE_SYSTEM`, `PUBLIC_MCU_COMM`, `PUBLIC_ANGLE_SENSOR_READ` |
|
|
100
|
+
| 2 | `0x10..0x17` | `PLAN_INVERSE_RESOLVE`, `PLAN_INVERSE_LIMIT`, `PLAN_DATA_REPEAT`, `PLAN_CURRENT_JOINT_OUT_OF_RANGE`, `PLAN_MOTION_TARGET_OUT_OF_WORKSPACE`, `PLAN_IN_SINGULARITY_ZONE` |
|
|
101
|
+
| 4 | `0x20..0x27` | `KINEMATIC_SINGULARITY`, `KINEMATIC_TARGET_OUT_OF_WORKSPACE`, `KINEMATIC_INVERSE_LIMIT` |
|
|
102
|
+
| 6 | `0x30..0x33` | `OVERSPEED_J1..J4` |
|
|
103
|
+
| 8 | `0x40..0x43` | `LIMIT_POS_J1..J4` (positive joint limit) |
|
|
104
|
+
| 10 | `0x50..0x53` | `LIMIT_NEG_J1..J4` (negative joint limit) |
|
|
105
|
+
| 12 | `0x60..0x63` | `LOST_STEP_J1..J4` |
|
|
106
|
+
| 14 | `0x70` | `OTHER_LIMIT_TRIGGERED_J1_J2` (auto-leveling switch) |
|
|
107
|
+
|
|
108
|
+
Each named alarm has a one-line troubleshoot tip surfaced via `AlarmSet.format()`. For example, `LIMIT_POS_J3` reads:
|
|
109
|
+
|
|
110
|
+
> `joint 3 (forearm) hit positive limit — jog Z down (or J3 negative), or call set_home()`
|
|
111
|
+
|
|
112
|
+
Unmapped bits are surfaced as `JOINT_LIMIT_POS_0x44` / `OVERSPEED_0x35` / `UNKNOWN_0x90` etc. so they're never silently dropped.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Recovery patterns
|
|
117
|
+
|
|
118
|
+
| Situation | What to do |
|
|
119
|
+
|-----------|-----------|
|
|
120
|
+
| Workspace / IK alarm (`PLAN_INVERSE_RESOLVE`, `PLAN_MOTION_TARGET_OUT_OF_WORKSPACE`, `KINEMATIC_*`) | The physical state is fine; the *target* was the problem. Call [`bot.clear_alarm()`](#clear-alarm), then retry with a reachable target. |
|
|
121
|
+
| Joint limit hit during JOG (`LIMIT_POS_J*` / `LIMIT_NEG_J*`) | The joint is *physically* against the stop. Jog away from the limit first, then `clear_alarm()`. Or just call [`bot.set_home()`](motion.md#set_home-waittrue-timeout600-raise_on_alarmtrue-int) — homing re-zeroes joints and clears the alarm in one step. |
|
|
122
|
+
| Lost step (`LOST_STEP_J*`) | Re-home to recalibrate. |
|
|
123
|
+
| Public / system fault (`PUBLIC_*`) | Power-cycle the arm (sometimes a re-home is enough for `PUBLIC_RESET`). |
|
|
124
|
+
|
|
125
|
+
The control panel's "Clear Alarm" button now calls [`clear_alarm()`](#clear-alarm) and re-polls 200 ms later; if the alarm comes back, it pops up an "Alarm persists" warning with the exact troubleshoot lines.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Connection lifecycle
|
|
2
|
+
|
|
3
|
+
Methods that open and close the serial / broker channel to the arm. The `Magician` constructor doesn't talk to hardware; that happens in `connect()`.
|
|
4
|
+
|
|
5
|
+
## `Magician(port=None, baudrate=115200, timeout=1.0, *, auto_start_queue=True, only_known_adapters=False, via_broker="auto", broker_host="127.0.0.1", broker_port=8765)`
|
|
6
|
+
|
|
7
|
+
**Purpose.** Construct a Magician driver. No I/O happens here.
|
|
8
|
+
|
|
9
|
+
See [Connecting to an arm](../getting-started/connect.md) for the full table of constructor options.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## `connect() -> None`
|
|
14
|
+
|
|
15
|
+
**Purpose.** Resolve the port, open the line, and prepare the firmware queue.
|
|
16
|
+
|
|
17
|
+
**Inputs.** None.
|
|
18
|
+
|
|
19
|
+
**Returns.** `None`.
|
|
20
|
+
|
|
21
|
+
**Raises.**
|
|
22
|
+
* [`DobotConnectionError`](errors.md#dobotconnectionerror) — port not found or open failed.
|
|
23
|
+
* [`DobotPortInUseError`](errors.md#dobotportinuseerror-extends-dobotconnectionerror) — a second Magician in this process already owns this port, or the OS refused exclusive access.
|
|
24
|
+
|
|
25
|
+
**Protocol.** No single command — at the wire level this is the OS-side `serial.Serial.open()` (or a TCP `ATTACH` to the broker), followed by:
|
|
26
|
+
* [`SET_QUEUED_CMD_CLEAR`](../protocol/command-ids.md) (245)
|
|
27
|
+
* [`SET_QUEUED_CMD_START_EXEC`](../protocol/command-ids.md) (240)
|
|
28
|
+
|
|
29
|
+
The two queue commands are skipped if you constructed with `auto_start_queue=False`.
|
|
30
|
+
|
|
31
|
+
**Example.**
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
bot = Magician(port="/dev/ttyUSB0")
|
|
35
|
+
bot.connect()
|
|
36
|
+
# … work …
|
|
37
|
+
bot.disconnect()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## `disconnect() -> None`
|
|
43
|
+
|
|
44
|
+
**Purpose.** Stop any running pose stream, stop the firmware queue, and close the transport.
|
|
45
|
+
|
|
46
|
+
**Inputs.** None.
|
|
47
|
+
|
|
48
|
+
**Returns.** `None`.
|
|
49
|
+
|
|
50
|
+
**Raises.** Doesn't raise — the close path swallows transport errors so you can call it from `finally:` blocks safely.
|
|
51
|
+
|
|
52
|
+
**Protocol.** Sends [`SET_QUEUED_CMD_STOP_EXEC`](../protocol/command-ids.md) (241) on the way out, then closes the OS handle (or sends a TCP close to the broker).
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## `__enter__() / __exit__()` — context-manager support
|
|
57
|
+
|
|
58
|
+
**Purpose.** Use `Magician` with `with`, which calls `connect()` on entry and `disconnect()` on exit even if an exception is raised inside the block.
|
|
59
|
+
|
|
60
|
+
**Example.**
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
with Magician() as bot:
|
|
64
|
+
bot.move_to(200, 0, 50, 0)
|
|
65
|
+
# Disconnected here — even if the move raised.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## `port` *(read-only property)*
|
|
71
|
+
|
|
72
|
+
**Purpose.** The OS-resolved port name once `connect()` has run. `None` before connection.
|
|
73
|
+
|
|
74
|
+
**Returns.** `str | None`.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## `is_open` *(read-only property)*
|
|
79
|
+
|
|
80
|
+
**Purpose.** `True` while the underlying transport is open.
|
|
81
|
+
|
|
82
|
+
**Returns.** `bool`.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Process-level helpers
|
|
87
|
+
|
|
88
|
+
These live in `pydobotlab.discovery` and are documented on the [Discovery](discovery.md) page, but you'll see them often when wrangling multi-arm setups:
|
|
89
|
+
|
|
90
|
+
* `is_port_in_use(port)` — `True` if some `Magician` in *this process* currently owns the port.
|
|
91
|
+
* `find_free_port(only_known_adapters=False)` — return the first port that answers like a Dobot and isn't already claimed.
|