hackerbot 0.2.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hackerbot Industries LLC
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,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: hackerbot
3
+ Version: 0.2.0
4
+ Summary: This module contains the setup for the hackerbot python package.
5
+ Author-email: Allen Chien <allen71090@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/hackerbotindustries/hackerbot-python-package
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: pyserial
12
+ Requires-Dist: pytest
13
+ Dynamic: license-file
14
+
15
+
16
+ # Hackerbot Python Package
17
+
18
+ Hackerbot python package (`hackerbot-python-package`) is a project that includes modules for controlling and managing the Hackerbot system.
19
+
20
+ ## Installation
21
+
22
+ Follow these steps to clone the repository and set up the required dependencies.
23
+
24
+ ### 1. Clone the Repository
25
+ Use SSH to clone the repository:
26
+ ```bash
27
+ https://github.com/hackerbotindustries/hackerbot-python-package.git
28
+ ```
29
+ This will create a directory named `hackerbot-python-package` and download all necessary files.
30
+
31
+ ### 2. Navigate to the Modules Directory
32
+ Move into the `hackerbot_modules` directory:
33
+ ```bash
34
+ cd hackerbot-python-package/
35
+ ```
36
+
37
+ ### 3. Install Dependencies
38
+ Install the `hackerbot` package using `pip`:
39
+ ```bash
40
+ pip install .
41
+ ```
42
+ This will install the package locally for your Python environment.
43
+
44
+ ## Usage
45
+ Once installed, you can import `hackerbot` in your Python scripts:
46
+ ```python
47
+ import hackerbot
48
+ ```
49
+
50
+ ### 4. Testing
51
+ To run the unit tests run:
52
+ ```bash
53
+ cd tests/unit_tests
54
+ pytest
55
+ ```
56
+
57
+ ## Troubleshooting
58
+ If you run into issues with the installation, try the following:
59
+ - Ensure you're using a virtual environment:
60
+ ```bash
61
+ python3 -m venv venv
62
+ source venv/bin/activate
63
+ ```
64
+ - Upgrade `pip` before installation:
65
+ ```bash
66
+ pip install --upgrade pip
67
+ ```
@@ -0,0 +1,53 @@
1
+
2
+ # Hackerbot Python Package
3
+
4
+ Hackerbot python package (`hackerbot-python-package`) is a project that includes modules for controlling and managing the Hackerbot system.
5
+
6
+ ## Installation
7
+
8
+ Follow these steps to clone the repository and set up the required dependencies.
9
+
10
+ ### 1. Clone the Repository
11
+ Use SSH to clone the repository:
12
+ ```bash
13
+ https://github.com/hackerbotindustries/hackerbot-python-package.git
14
+ ```
15
+ This will create a directory named `hackerbot-python-package` and download all necessary files.
16
+
17
+ ### 2. Navigate to the Modules Directory
18
+ Move into the `hackerbot_modules` directory:
19
+ ```bash
20
+ cd hackerbot-python-package/
21
+ ```
22
+
23
+ ### 3. Install Dependencies
24
+ Install the `hackerbot` package using `pip`:
25
+ ```bash
26
+ pip install .
27
+ ```
28
+ This will install the package locally for your Python environment.
29
+
30
+ ## Usage
31
+ Once installed, you can import `hackerbot` in your Python scripts:
32
+ ```python
33
+ import hackerbot
34
+ ```
35
+
36
+ ### 4. Testing
37
+ To run the unit tests run:
38
+ ```bash
39
+ cd tests/unit_tests
40
+ pytest
41
+ ```
42
+
43
+ ## Troubleshooting
44
+ If you run into issues with the installation, try the following:
45
+ - Ensure you're using a virtual environment:
46
+ ```bash
47
+ python3 -m venv venv
48
+ source venv/bin/activate
49
+ ```
50
+ - Upgrade `pip` before installation:
51
+ ```bash
52
+ pip install --upgrade pip
53
+ ```
@@ -0,0 +1,44 @@
1
+ ################################################################################
2
+ # Copyright (c) 2025 Hackerbot Industries LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+ # Created By: Allen Chien
8
+ # Created: April 2025
9
+ # Updated: 2025.04.01
10
+ #
11
+ # This module contains the setup details for the hackerbot python package.
12
+ #
13
+ # Special thanks to the following for their code contributions to this codebase:
14
+ # Allen Chien - https://github.com/AllenChienXXX
15
+ ################################################################################
16
+
17
+
18
+ [project]
19
+ name = "hackerbot"
20
+ version = "0.2.0"
21
+ description = "This module contains the setup for the hackerbot python package."
22
+ authors = [
23
+ { name = "Allen Chien", email = "allen71090@gmail.com" }
24
+ ]
25
+ license = "MIT"
26
+ readme = "README.md"
27
+ requires-python = ">=3.11"
28
+ dependencies = [
29
+ "pyserial",
30
+ "pytest",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/hackerbotindustries/hackerbot-python-package"
35
+
36
+ [build-system]
37
+ requires = ["setuptools>=61.0"]
38
+ build-backend = "setuptools.build_meta"
39
+
40
+ [tool.setuptools]
41
+ package-dir = {"" = "src"}
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,33 @@
1
+ ################################################################################
2
+ # Copyright (c) 2025 Hackerbot Industries LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+ # Created By: Allen Chien
8
+ # Created: April 2025
9
+ # Updated: 2025.04.08
10
+ #
11
+ # This is the file for the hackerbot package. It imports
12
+ # and initialized the sub components
13
+ #
14
+ # Special thanks to the following for their code contributions to this codebase:
15
+ # Allen Chien - https://github.com/AllenChienXXX
16
+ ################################################################################
17
+
18
+
19
+ from .core import Core
20
+ from .base import Base
21
+ from .head import Head
22
+ from .arm import Arm
23
+ from .utils.hackerbot_helper import HackerbotHelper
24
+
25
+ class Hackerbot(HackerbotHelper):
26
+ def __init__(self, port=None, board=None, model=None,verbose_mode=False):
27
+ super().__init__(port, board, verbose_mode)
28
+ # Share self (which is a HackerbotHelper) with subsystems
29
+ self.core = Core(controller=self)
30
+ self.base = Base(controller=self)
31
+ self.head = Head(controller=self)
32
+ self.arm = Arm(controller=self)
33
+ # TODO based on model decide which subsystems to initialize
@@ -0,0 +1,77 @@
1
+ ################################################################################
2
+ # Copyright (c) 2025 Hackerbot Industries LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+ # Created By: Allen Chien
8
+ # Created: April 2025
9
+ # Updated: 2025.04.07
10
+ #
11
+ # This module contains the Arm component of the hackerbot
12
+ #
13
+ # Special thanks to the following for their code contributions to this codebase:
14
+ # Allen Chien - https://github.com/AllenChienXXX
15
+ ################################################################################
16
+
17
+
18
+ from hackerbot.utils.hackerbot_helper import HackerbotHelper
19
+ from .gripper import Gripper
20
+
21
+ class Arm():
22
+ def __init__(self, controller: HackerbotHelper):
23
+ self._controller = controller
24
+ self.idle_mode = True
25
+
26
+ self.setup()
27
+ self.gripper = Gripper(self._controller)
28
+
29
+ def setup(self):
30
+ if not self._controller._arm_attached:
31
+ self._controller.log_warning("Arm not attached, can't control arm.")
32
+
33
+ def move_joint(self, joint_id, angle, speed):
34
+ """
35
+ Moves a single joint of the robotic arm to a specified angle at a given speed.
36
+
37
+ Args:
38
+ joint_id (int): Joint number from 1 to 6. Joint 1 is the base and is in order moving up the arm.
39
+ angle (float): Angle for the specified joint. Valid range is -165.0 to 165.0 degrees for joints 1 to 5 and -175.0 to 175.0 for joint 6.
40
+ speed (int): Speed at which the arm moves to the new position. Valid range is 0 to 100.
41
+
42
+ Returns:
43
+ bool: True if the movement command was successfully sent, False if an error occurred.
44
+ """
45
+ try:
46
+ self._controller.send_raw_command(f"A_ANGLE,{joint_id},{angle},{speed}")
47
+ # Not fetching json response since machine mode not implemented
48
+ return True
49
+ except Exception as e:
50
+ self._controller.log_error(f"Error in arm:move_joint: {e}")
51
+ return False
52
+
53
+ def move_joints(self, j_agl_1, j_agl_2, j_agl_3, j_agl_4, j_agl_5, j_agl_6, speed):
54
+ """
55
+ Moves all six joints of the robotic arm to specified angles at a given speed.
56
+
57
+ Args:
58
+ j_agl_1 (float): Angle for joint 1, base joint. Valid range is -165.0 to 165.0 degrees.
59
+ j_agl_2 (float): Angle for joint 2. Valid range is -165.0 to 165.0 degrees.
60
+ j_agl_3 (float): Angle for joint 3. Valid range is -165.0 to 165.0 degrees.
61
+ j_agl_4 (float): Angle for joint 4. Valid range is -165.0 to 165.0 degrees.
62
+ j_agl_5 (float): Angle for joint 5. Valid range is -165.0 to 165.0 degrees.
63
+ j_agl_6 (float): Angle for joint 6. Valid range is -175.0 to 175.0 degrees.
64
+ speed (int): Speed at which the arm moves to the new positions. Valid range is 0 to 100.
65
+
66
+ Returns:
67
+ bool: True if the movement command was successfully sent, False if an error occurred.
68
+ """
69
+ try:
70
+ self._controller.send_raw_command(f"A_ANGLES,{j_agl_1},{j_agl_2},{j_agl_3},{j_agl_4},{j_agl_5},{j_agl_6},{speed}")
71
+ # Not fetching json response since machine mode not implemented
72
+ return True
73
+ except Exception as e:
74
+ self._controller.log_error(f"Error in arm:move_joints: {e}")
75
+ return False
76
+
77
+
@@ -0,0 +1,55 @@
1
+ ################################################################################
2
+ # Copyright (c) 2025 Hackerbot Industries LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+ # Created By: Allen Chien
8
+ # Created: April 2025
9
+ # Updated: 2025.04.07
10
+ #
11
+ # This module contains the Gripper component of the hackerbot
12
+ #
13
+ # Special thanks to the following for their code contributions to this codebase:
14
+ # Allen Chien - https://github.com/AllenChienXXX
15
+ ################################################################################
16
+
17
+
18
+ from hackerbot.utils.hackerbot_helper import HackerbotHelper
19
+
20
+ class Gripper(HackerbotHelper):
21
+ def __init__(self, controller: HackerbotHelper):
22
+ self._controller = controller
23
+
24
+ def calibrate(self):
25
+ """
26
+ Calibrates the gripper by sending a raw calibration command.
27
+
28
+ Returns:
29
+ bool: True if the calibration command was successfully sent, False if an error occurred.
30
+ """
31
+ try:
32
+ self._controller.send_raw_command("A_CAL")
33
+ # Not fetching json response since machine mode not implemented
34
+ return True
35
+ except Exception as e:
36
+ self._controller.log_error(f"Error in gripper:calibrate: {e}")
37
+ return False
38
+
39
+ def open(self):
40
+ try:
41
+ self._controller.send_raw_command("A_OPEN")
42
+ # Not fetching json response since machine mode not implemented
43
+ return True
44
+ except Exception as e:
45
+ self._controller.log_error(f"Error in gripper:open: {e}")
46
+ return False
47
+
48
+ def close(self):
49
+ try:
50
+ self._controller.send_raw_command("A_CLOSE")
51
+ # Not fetching json response since machine mode not implemented
52
+ return True
53
+ except Exception as e:
54
+ self._controller.log_error(f"Error in gripper:close: {e}")
55
+ return False
@@ -0,0 +1,223 @@
1
+ ################################################################################
2
+ # Copyright (c) 2025 Hackerbot Industries LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+ # Created By: Allen Chien
8
+ # Created: April 2025
9
+ # Updated: 2025.04.08
10
+ #
11
+ # This module contains the Base component of the hackerbot
12
+ #
13
+ # Special thanks to the following for their code contributions to this codebase:
14
+ # Allen Chien - https://github.com/AllenChienXXX
15
+ ################################################################################
16
+
17
+
18
+ from hackerbot.utils.hackerbot_helper import HackerbotHelper
19
+ from .maps import Maps
20
+ import time
21
+
22
+ class Base():
23
+ def __init__(self, controller: HackerbotHelper):
24
+ """
25
+ Initialize Core component with HackerbotHelper object
26
+
27
+ :param controller: HackerbotHelper object
28
+ """
29
+ self._controller = controller
30
+ self.initialize() # Call before any action is done on the base
31
+
32
+ self.maps = Maps(controller)
33
+
34
+ self._future_completed = False
35
+ self._docked = True # Default to true, assume always start from charger
36
+
37
+
38
+ def initialize(self):
39
+ try:
40
+ self._controller.send_raw_command("B_INIT")
41
+ self._controller._base_init = True
42
+ # Not fetching json response since machine mode not implemented
43
+ return True
44
+ except Exception as e:
45
+ self._controller.log_error(f"Error in base:initialize: {e}")
46
+ raise Exception(f"Error in initialize: {e}")
47
+
48
+ def set_mode(self, mode):
49
+ try:
50
+ self._controller.send_raw_command(f"B_MODE,{mode}")
51
+ # Not fetching json response since machine mode not implemented
52
+ return True
53
+ except Exception as e:
54
+ self._controller.log_error(f"Error in base:set_mode: {e}")
55
+ return False
56
+
57
+ def status(self):
58
+ try:
59
+ self._controller.send_raw_command("B_STATUS")
60
+ time.sleep(0.1)
61
+ response = self._controller.get_json_from_command("status")
62
+ if response is None:
63
+ raise Exception("Status command failed")
64
+
65
+ if response.get("left_set_speed") == 0 and response.get("right_set_speed") == 0:
66
+ self._future_completed = True
67
+ else:
68
+ self._future_completed = False
69
+
70
+ # Parse and return relevant fields
71
+ parsed_data = {
72
+ "timestamp": response.get("timestamp"),
73
+ "left_encoder": response.get("left_encoder"),
74
+ "right_encoder": response.get("right_encoder"),
75
+ "left_speed": response.get("left_speed"),
76
+ "right_speed": response.get("right_speed"),
77
+ "left_set_speed": response.get("left_set_speed"),
78
+ "right_set_speed": response.get("right_set_speed"),
79
+ "wall_tof": response.get("wall_tof"),
80
+ }
81
+ return parsed_data
82
+ except Exception as e:
83
+ self._controller.log_error(f"Error in base:status: {e}")
84
+ return None
85
+
86
+ def start(self, block=True):
87
+ try:
88
+ self._controller.send_raw_command("B_START")
89
+ # Not fetching json response since machine mode not implemented
90
+ self._controller._driver_mode = True
91
+ if self._docked:
92
+ time.sleep(2)
93
+ self._docked = False
94
+ self._wait_until_completed(block=block)
95
+
96
+ return True
97
+ except Exception as e:
98
+ self._controller.log_error(f"Error in base:start: {e}")
99
+ return False
100
+
101
+ def quickmap(self, block=True):
102
+ """
103
+ Start the quick mapping process.
104
+
105
+ This function sends a command to the base to initiate the quick mapping process.
106
+ It first checks the system status to ensure all components are ready. If
107
+ the quick mapping command is successfully sent, the function returns True.
108
+ In case of any errors, it logs the error message and returns False.
109
+
110
+ :return: True if the quick mapping command is successful, False otherwise.
111
+ """
112
+ try:
113
+ self._controller.send_raw_command("B_QUICKMAP")
114
+ time.sleep(0.1)
115
+ # Not fetching json response since machine mode not implemented
116
+ self._wait_until_completed(block=block)
117
+ return True
118
+ except Exception as e:
119
+ self._controller.log_error(f"Error in base:quickmap: {e}")
120
+ return False
121
+
122
+ def dock(self, block=True):
123
+ """
124
+ Dock the base to the docking station.
125
+
126
+ This function sends a command to the base to initiate the docking process.
127
+ It first checks the system status to ensure all components are ready. If
128
+ the docking command is successfully sent, the function returns True.
129
+ In case of any errors, it logs the error message and returns False.
130
+
131
+ :return: True if the docking command is successful, False otherwise.
132
+ """
133
+ try:
134
+ self._controller.send_raw_command("B_DOCK")
135
+ time.sleep(3)
136
+ # Not fetching json response since machine mode not implemented
137
+ self._wait_until_completed(block=block)
138
+ self._docked = True
139
+ self._controller._driver_mode = False
140
+ return True
141
+ except Exception as e:
142
+ self._controller.log_error(f"Error in base:dock: {e}")
143
+ return False
144
+
145
+
146
+ def kill(self):
147
+ """
148
+ Kill the base's movement. This is a blocking call and will not return until the base is stopped.
149
+ After calling this method, the base will not be able to move until start() is called again.
150
+ :return: True if successful, False otherwise.
151
+ """
152
+ try:
153
+ self._controller.send_raw_command("B_KILL")
154
+ self._controller._base_init = False
155
+ # Not fetching json response since machine mode not implemented
156
+ return True
157
+ except Exception as e:
158
+ self._controller.log_error(f"Error in base:kill: {e}")
159
+ return False
160
+
161
+ def trigger_bump(self, left, right):
162
+ """
163
+ Trigger the bump sensors on the base.
164
+
165
+ :param left: 0 or 1 to disable or enable the left bump sensor.
166
+ :param right: 0 or 1 to disable or enable the right bump sensor.
167
+ :return: True if the command is successful, False if it fails.
168
+ """
169
+ left = 1 if True else 0
170
+ right = 1 if True else 0
171
+ try:
172
+ self._controller.send_raw_command("B_BUMP, {0}, {1}".format(left, right))
173
+ # Not fetching json response since machine mode not implemented
174
+ return True
175
+ except Exception as e:
176
+ self._controller.log_error(f"Error in base:trigger_bump: {e}")
177
+ return False
178
+
179
+ def drive(self, l_vel, a_vel, block=True):
180
+ """
181
+ Set the base velocity.
182
+
183
+ :param l_vel: Linear velocity in mm/s. Positive is forward, negative is backward.
184
+ :param a_vel: Angular velocity in degrees/s. Positive is counterclockwise, negative is clockwise.
185
+ :return: True if the command is successful, False if it fails.
186
+ """
187
+ try:
188
+ if not self._controller._driver_mode:
189
+ self.start()
190
+ self._controller.send_raw_command(f"B_DRIVE,{l_vel},{a_vel}")
191
+ time.sleep(0.1)
192
+ response = self._controller.get_json_from_command("drive")
193
+ if response is None:
194
+ raise Exception("Drive command failed")
195
+ self._wait_until_completed(block=block)
196
+ return True
197
+ except Exception as e:
198
+ self._controller.log_error(f"Error in base:drive: {e}")
199
+ return False
200
+
201
+ def _wait_until_completed(self, block=True):
202
+ if not block:
203
+ return
204
+ while not self._future_completed:
205
+ self.status()
206
+ # print(self.status())
207
+ self._future_completed = False
208
+
209
+ def destroy(self, auto_dock=False):
210
+ """
211
+ Clean up and shut down the base.
212
+
213
+ This method kills the base's movement and optionally docks it before
214
+ destroying the controller. If `auto_dock` is set to True, the base will
215
+ dock before the destruction process.
216
+
217
+ :param auto_dock: If True, the base will dock before being destroyed. Defaults to False.
218
+ """
219
+ self.kill()
220
+ if auto_dock:
221
+ time.sleep(3.0)
222
+ self.dock(block=False)
223
+ self._controller.destroy()