hackerbot 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hackerbot/__init__.py +33 -0
- hackerbot/arm/__init__.py +77 -0
- hackerbot/arm/gripper.py +55 -0
- hackerbot/base/__init__.py +223 -0
- hackerbot/base/maps.py +147 -0
- hackerbot/core.py +120 -0
- hackerbot/examples/keyboard_teleop_examples/AI_ELITE_teleop.py +219 -0
- hackerbot/examples/keyboard_teleop_examples/AI_PRO_teleop.py +180 -0
- hackerbot/examples/keyboard_teleop_examples/arm_teleop.py +230 -0
- hackerbot/examples/keyboard_teleop_examples/base_teleop.py +206 -0
- hackerbot/examples/keyboard_teleop_examples/head_teleop.py +170 -0
- hackerbot/head/__init__.py +61 -0
- hackerbot/head/eyes.py +41 -0
- hackerbot/utils/hackerbot_helper.py +142 -0
- hackerbot/utils/serial_helper.py +156 -0
- hackerbot-0.2.0.dist-info/METADATA +67 -0
- hackerbot-0.2.0.dist-info/RECORD +20 -0
- hackerbot-0.2.0.dist-info/WHEEL +5 -0
- hackerbot-0.2.0.dist-info/licenses/LICENSE +21 -0
- hackerbot-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,219 @@
|
|
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 script is an example of how to use the AI_ELITE_teleop behavior.
|
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 import Hackerbot
|
19
|
+
import time
|
20
|
+
import os
|
21
|
+
|
22
|
+
import sys
|
23
|
+
from select import select
|
24
|
+
from arm_teleop import ArmTeleop
|
25
|
+
from base_teleop import BaseTeleop, KBHit
|
26
|
+
from head_teleop import HeadTeleop
|
27
|
+
|
28
|
+
class AI_ELITE_Teleop(ArmTeleop, BaseTeleop, HeadTeleop):
|
29
|
+
def __init__(self):
|
30
|
+
self.kb = KBHit()
|
31
|
+
|
32
|
+
self.robot = Hackerbot()
|
33
|
+
self.robot.base.initialize()
|
34
|
+
|
35
|
+
# Modify movement parameters
|
36
|
+
self.step_size = 0.2 # mm
|
37
|
+
self.max_l_step_size = 300.0 # mm/s
|
38
|
+
self.max_r_step_size = 90.0 # degree/s
|
39
|
+
|
40
|
+
self.joint_speed = 50
|
41
|
+
|
42
|
+
self.j_agl_1 = 0
|
43
|
+
self.j_agl_2 = 0
|
44
|
+
self.j_agl_3 = 0
|
45
|
+
self.j_agl_4 = 0
|
46
|
+
self.j_agl_5 = 0
|
47
|
+
self.j_agl_6 = 0
|
48
|
+
|
49
|
+
self.yaw = 180
|
50
|
+
self.pitch = 180
|
51
|
+
|
52
|
+
self.base_command = None
|
53
|
+
self.head_command = None
|
54
|
+
self.arm_command = None
|
55
|
+
|
56
|
+
self.stop = False
|
57
|
+
self.last_key = None # Track last keypress
|
58
|
+
|
59
|
+
# Print initial instructions to terminal
|
60
|
+
self.print_terminal_instructions()
|
61
|
+
|
62
|
+
def print_terminal_instructions(self):
|
63
|
+
"""Print instructions to the terminal"""
|
64
|
+
os.system('clear' if os.name == 'posix' else 'cls')
|
65
|
+
print("\n" + "="*10 + " Robot Teleop Controls " + "="*10 + "\r")
|
66
|
+
print("Base controls:\r")
|
67
|
+
print(" ↑ / ↓ : FWD/BCK | ← / → : L/R | space : STOP\r")
|
68
|
+
print("=" * 30 + "\r")
|
69
|
+
print("Head Controls:\r")
|
70
|
+
print(" u/i : Yaw L/R | j/k : Pitch U/D")
|
71
|
+
print("=" * 30 + "\r")
|
72
|
+
print("Arm Controls:\r")
|
73
|
+
print(" q/w : Joint 1 L/R | a/s : Joint 2 BCK/FWD | z/x : Joint 3 BCK/FWD")
|
74
|
+
print(" e/r : Joint 4 BCK/FWD | d/f : Joint 5 L/R | c/v : Joint 6 L/R")
|
75
|
+
print("\nGripper:")
|
76
|
+
print(" g/h : Open/Close gripper | b : Calibrate gripper\r")
|
77
|
+
|
78
|
+
print("\nOther:")
|
79
|
+
print(" o/p : increase/decrease step size | -/+ : decrease/increase speed\r")
|
80
|
+
print("\nCTRL-C or 0 to quit\r")
|
81
|
+
print("=" * 43 + "\r")
|
82
|
+
|
83
|
+
def update_display(self):
|
84
|
+
"""Update step size and speed in place without adding new lines"""
|
85
|
+
sys.stdout.write(f"\rCurrent step size: {self.step_size:.1f} | Current joint speed: {self.joint_speed} ")
|
86
|
+
sys.stdout.flush() # Ensure immediate update
|
87
|
+
|
88
|
+
def get_command(self):
|
89
|
+
self.base_command = False
|
90
|
+
self.head_command = False
|
91
|
+
self.arm_command = False
|
92
|
+
key = None
|
93
|
+
# Read keyboard input
|
94
|
+
if self.kb.kbhit() is not None:
|
95
|
+
key = self.kb.getch()
|
96
|
+
# print(f"key: {key}\r")
|
97
|
+
while sys.stdin in select([sys.stdin], [], [], 0)[0]:
|
98
|
+
sys.stdin.read(1)
|
99
|
+
|
100
|
+
if key == self.last_key:
|
101
|
+
self.last_key = None
|
102
|
+
return None, None
|
103
|
+
|
104
|
+
self.last_key = key # Update last key
|
105
|
+
|
106
|
+
# Check for quit conditions
|
107
|
+
if key == '0': # '0' key to quit
|
108
|
+
self.stop = True
|
109
|
+
return None, None
|
110
|
+
|
111
|
+
if key == 'o':
|
112
|
+
self.step_size += 0.1
|
113
|
+
if self.step_size > 1:
|
114
|
+
self.step_size = 1
|
115
|
+
elif key == 'p':
|
116
|
+
self.step_size -= 0.1
|
117
|
+
if self.step_size < 0.1:
|
118
|
+
self.step_size = 0.1
|
119
|
+
elif key == '-':
|
120
|
+
self.joint_speed -= 10
|
121
|
+
if self.joint_speed < 10:
|
122
|
+
self.joint_speed = 10
|
123
|
+
elif key == '+':
|
124
|
+
self.joint_speed += 10
|
125
|
+
if self.joint_speed > 100:
|
126
|
+
self.joint_speed = 100
|
127
|
+
if key in ['\x1b[A', '\x1b[B', '\x1b[D', '\x1b[C', ' ']:
|
128
|
+
l_vel, r_vel = BaseTeleop.get_base_command_from_key(self, key)
|
129
|
+
self.base_command = True
|
130
|
+
return l_vel, r_vel
|
131
|
+
elif key in ['u', 'i', 'j', 'k']:
|
132
|
+
y, p = HeadTeleop.get_head_value_from_key(self, key)
|
133
|
+
self.head_command = True
|
134
|
+
return y, p
|
135
|
+
elif key in ['q', 'w', 'a', 's', 'z', 'x', 'e', 'r', 'd', 'f', 'c', 'v', 'g', 'h', 'b']:
|
136
|
+
command, value = ArmTeleop.get_arm_value_from_key(self, key)
|
137
|
+
self.arm_command = True
|
138
|
+
return command, value
|
139
|
+
else:
|
140
|
+
return None, None
|
141
|
+
|
142
|
+
else:
|
143
|
+
self.last_key = None
|
144
|
+
return 0.0, 0.0
|
145
|
+
|
146
|
+
def run(self):
|
147
|
+
while not self.stop:
|
148
|
+
input_1, input_2 = self.get_command()
|
149
|
+
if input_1 is not None and input_2 is not None:
|
150
|
+
response = None # Initialize response
|
151
|
+
if self.base_command:
|
152
|
+
response = self.robot.base.drive(input_1, input_2, block=False)
|
153
|
+
time.sleep(0.01)
|
154
|
+
elif self.head_command:
|
155
|
+
response = self.robot.head.look(input_1, input_2, self.joint_speed)
|
156
|
+
time.sleep(0.01)
|
157
|
+
elif self.arm_command:
|
158
|
+
command = input_1
|
159
|
+
value = input_2
|
160
|
+
if isinstance(command, int): # Joint movement
|
161
|
+
# Limit joint angles based on which joint
|
162
|
+
max_angle = 175.0 if command == 6 else 165.0
|
163
|
+
if abs(value) <= max_angle:
|
164
|
+
response = self.robot.arm.move_joint(command, value, self.joint_speed)
|
165
|
+
elif command == 'gripper_open':
|
166
|
+
response = self.robot.arm.gripper.open()
|
167
|
+
elif command == 'gripper_close':
|
168
|
+
response = self.robot.arm.gripper.close()
|
169
|
+
elif command == 'gripper_calibrate':
|
170
|
+
response = self.robot.arm.gripper.calibrate()
|
171
|
+
|
172
|
+
time.sleep(0.2)
|
173
|
+
|
174
|
+
if response == False:
|
175
|
+
break
|
176
|
+
|
177
|
+
input_1 = None
|
178
|
+
input_2 = None
|
179
|
+
self.update_display()
|
180
|
+
|
181
|
+
def stow(self):
|
182
|
+
self.robot.arm.move_joints(0,0,0,0,0,0,50)
|
183
|
+
self.robot.head.look(180,180,50)
|
184
|
+
|
185
|
+
def cleanup(self):
|
186
|
+
"""Cleanup method to properly shut down the robot and restore terminal settings"""
|
187
|
+
try:
|
188
|
+
# Restore terminal settings
|
189
|
+
self.kb.set_normal_term()
|
190
|
+
# self.robot.stop_driver()
|
191
|
+
self.stow()
|
192
|
+
# Destroy the robot connection
|
193
|
+
self.robot.base.destroy(auto_dock=True)
|
194
|
+
|
195
|
+
except Exception as e:
|
196
|
+
print(f"\nError during cleanup: {e}")
|
197
|
+
# Try to restore terminal settings even if there's an error
|
198
|
+
try:
|
199
|
+
self.kb.set_normal_term()
|
200
|
+
except:
|
201
|
+
pass
|
202
|
+
|
203
|
+
def __del__(self):
|
204
|
+
"""Destructor to ensure cleanup is called"""
|
205
|
+
self.cleanup()
|
206
|
+
|
207
|
+
# Main entry point
|
208
|
+
if __name__ == '__main__':
|
209
|
+
teleop = None
|
210
|
+
try:
|
211
|
+
teleop = AI_ELITE_Teleop()
|
212
|
+
teleop.run()
|
213
|
+
except KeyboardInterrupt:
|
214
|
+
print("\nShutting down...")
|
215
|
+
except Exception as e:
|
216
|
+
print(f"\nError: {e}")
|
217
|
+
finally:
|
218
|
+
if teleop:
|
219
|
+
teleop.cleanup()
|
@@ -0,0 +1,180 @@
|
|
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 script is an example of how to use the AI_PRO_teleop behavior.
|
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 import Hackerbot
|
19
|
+
import time
|
20
|
+
import os
|
21
|
+
|
22
|
+
import sys
|
23
|
+
from select import select
|
24
|
+
from base_teleop import BaseTeleop, KBHit
|
25
|
+
from head_teleop import HeadTeleop
|
26
|
+
|
27
|
+
class AI_PRO_Teleop():
|
28
|
+
def __init__(self):
|
29
|
+
self.kb = KBHit()
|
30
|
+
|
31
|
+
self.robot = Hackerbot()
|
32
|
+
self.robot.base.initialize()
|
33
|
+
|
34
|
+
# Modify movement parameters
|
35
|
+
self.step_size = 0.2 # mm
|
36
|
+
self.max_l_step_size = 300.0 # mm/s
|
37
|
+
self.max_r_step_size = 90.0 # degree/s
|
38
|
+
|
39
|
+
self.joint_speed = 50
|
40
|
+
|
41
|
+
self.yaw = 180
|
42
|
+
self.pitch = 180
|
43
|
+
|
44
|
+
self.base_command = None
|
45
|
+
self.head_command = None
|
46
|
+
|
47
|
+
self.stop = False
|
48
|
+
self.last_key = None # Track last keypress
|
49
|
+
|
50
|
+
# Print initial instructions to terminal
|
51
|
+
self.print_terminal_instructions()
|
52
|
+
|
53
|
+
def print_terminal_instructions(self):
|
54
|
+
"""Print instructions to the terminal"""
|
55
|
+
os.system('clear' if os.name == 'posix' else 'cls')
|
56
|
+
print("\n" + "="*10 + " Robot Teleop Controls " + "="*10 + "\r")
|
57
|
+
print("Base controls:\r")
|
58
|
+
print(" ↑ / ↓ : FWD/BCK | ← / → : L/R | space : STOP\r")
|
59
|
+
print("=" * 30 + "\r")
|
60
|
+
print("Head Controls:\r")
|
61
|
+
print(" u/i : Yaw L/R | j/k : Pitch UP/DN")
|
62
|
+
|
63
|
+
print("\nOther:")
|
64
|
+
print(" o/p : increase/decrease step size | -/+ : decrease/increase speed\r")
|
65
|
+
print("\nCTRL-C or 0 to quit\r")
|
66
|
+
print("=" * 43 + "\r")
|
67
|
+
|
68
|
+
def update_display(self):
|
69
|
+
"""Update step size and speed in place without adding new lines"""
|
70
|
+
sys.stdout.write(f"\rCurrent step size: {self.step_size:.1f} | Current joint speed: {self.joint_speed} ")
|
71
|
+
sys.stdout.flush() # Ensure immediate update
|
72
|
+
|
73
|
+
def get_command(self):
|
74
|
+
self.base_command = False
|
75
|
+
self.head_command = False
|
76
|
+
key = None
|
77
|
+
# Read keyboard input
|
78
|
+
if self.kb.kbhit() is not None:
|
79
|
+
key = self.kb.getch()
|
80
|
+
# print(f"key: {key}\r")
|
81
|
+
while sys.stdin in select([sys.stdin], [], [], 0)[0]:
|
82
|
+
sys.stdin.read(1)
|
83
|
+
|
84
|
+
if key == self.last_key:
|
85
|
+
self.last_key = None
|
86
|
+
return None, None
|
87
|
+
|
88
|
+
self.last_key = key # Update last key
|
89
|
+
|
90
|
+
# Check for quit conditions
|
91
|
+
if key == '0': # '0' key to quit
|
92
|
+
self.stop = True
|
93
|
+
return None, None
|
94
|
+
|
95
|
+
if key == 'o':
|
96
|
+
self.step_size += 0.1
|
97
|
+
if self.step_size > 1:
|
98
|
+
self.step_size = 1
|
99
|
+
elif key == 'p':
|
100
|
+
self.step_size -= 0.1
|
101
|
+
if self.step_size < 0.1:
|
102
|
+
self.step_size = 0.1
|
103
|
+
elif key == '-':
|
104
|
+
self.joint_speed -= 10
|
105
|
+
if self.joint_speed < 10:
|
106
|
+
self.joint_speed = 10
|
107
|
+
elif key == '+':
|
108
|
+
self.joint_speed += 10
|
109
|
+
if self.joint_speed > 100:
|
110
|
+
self.joint_speed = 100
|
111
|
+
if key in ['\x1b[A', '\x1b[B', '\x1b[D', '\x1b[C', ' ']:
|
112
|
+
l_vel, r_vel = BaseTeleop.get_base_command_from_key(self, key)
|
113
|
+
self.base_command = True
|
114
|
+
return l_vel, r_vel
|
115
|
+
elif key in ['u', 'i', 'j', 'k']:
|
116
|
+
y, p = HeadTeleop.get_head_value_from_key(self, key)
|
117
|
+
self.head_command = True
|
118
|
+
return y, p
|
119
|
+
else:
|
120
|
+
return None, None
|
121
|
+
|
122
|
+
else:
|
123
|
+
self.last_key = None
|
124
|
+
return 0.0, 0.0
|
125
|
+
|
126
|
+
def run(self):
|
127
|
+
while not self.stop:
|
128
|
+
input_1, input_2 = self.get_command()
|
129
|
+
if input_1 is not None and input_2 is not None:
|
130
|
+
response = None # Initialize response
|
131
|
+
if self.base_command:
|
132
|
+
response = self.robot.base.drive(input_1, input_2, block=False)
|
133
|
+
time.sleep(0.01)
|
134
|
+
elif self.head_command:
|
135
|
+
response = self.robot.head.look(input_1, input_2, self.joint_speed)
|
136
|
+
time.sleep(0.01)
|
137
|
+
if response == False:
|
138
|
+
break
|
139
|
+
|
140
|
+
input_1 = None
|
141
|
+
input_2 = None
|
142
|
+
self.update_display()
|
143
|
+
|
144
|
+
def stow(self):
|
145
|
+
self.robot.head.look(180,180,50)
|
146
|
+
|
147
|
+
def cleanup(self):
|
148
|
+
"""Cleanup method to properly shut down the robot and restore terminal settings"""
|
149
|
+
try:
|
150
|
+
# Restore terminal settings
|
151
|
+
self.kb.set_normal_term()
|
152
|
+
self.stow()
|
153
|
+
# Destroy the robot connection
|
154
|
+
self.robot.base.destroy(auto_dock=True)
|
155
|
+
|
156
|
+
except Exception as e:
|
157
|
+
print(f"\nError during cleanup: {e}")
|
158
|
+
# Try to restore terminal settings even if there's an error
|
159
|
+
try:
|
160
|
+
self.kb.set_normal_term()
|
161
|
+
except:
|
162
|
+
pass
|
163
|
+
|
164
|
+
def __del__(self):
|
165
|
+
"""Destructor to ensure cleanup is called"""
|
166
|
+
self.cleanup()
|
167
|
+
|
168
|
+
# Main entry point
|
169
|
+
if __name__ == '__main__':
|
170
|
+
teleop = None
|
171
|
+
try:
|
172
|
+
teleop = AI_PRO_Teleop()
|
173
|
+
teleop.run()
|
174
|
+
except KeyboardInterrupt:
|
175
|
+
print("\nShutting down...")
|
176
|
+
except Exception as e:
|
177
|
+
print(f"\nError: {e}")
|
178
|
+
finally:
|
179
|
+
if teleop:
|
180
|
+
teleop.cleanup()
|
@@ -0,0 +1,230 @@
|
|
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 script is an example of how to use the arm teleop behavior.
|
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 import Hackerbot
|
19
|
+
import time
|
20
|
+
import os
|
21
|
+
|
22
|
+
import sys
|
23
|
+
from select import select
|
24
|
+
from base_teleop import KBHit
|
25
|
+
|
26
|
+
class ArmTeleop:
|
27
|
+
def __init__(self):
|
28
|
+
self.kb = KBHit()
|
29
|
+
|
30
|
+
self.robot = Hackerbot()
|
31
|
+
self.robot.base.initialize()
|
32
|
+
|
33
|
+
|
34
|
+
# Modify movement parameters
|
35
|
+
self.arm_speed = 50
|
36
|
+
self.step_size = 0.2 # mm
|
37
|
+
|
38
|
+
self.j_agl_1 = 0
|
39
|
+
self.j_agl_2 = 0
|
40
|
+
self.j_agl_3 = 0
|
41
|
+
self.j_agl_4 = 0
|
42
|
+
self.j_agl_5 = 0
|
43
|
+
self.j_agl_6 = 0
|
44
|
+
|
45
|
+
self.stop = False
|
46
|
+
self.last_key = None # Track last keypress
|
47
|
+
|
48
|
+
# Print initial instructions to terminal
|
49
|
+
self.print_terminal_instructions()
|
50
|
+
|
51
|
+
def print_terminal_instructions(self):
|
52
|
+
"""Print static instructions to the terminal"""
|
53
|
+
os.system('clear' if os.name == 'posix' else 'cls')
|
54
|
+
print("\n" + "="*10 + " Robot Arm Teleop Controls " + "="*10 + "\r")
|
55
|
+
|
56
|
+
print("\nJoint Controls (±165° for joints 1-5, ±175° for joint 6):")
|
57
|
+
print(" q/w : Joint 1 L/R")
|
58
|
+
print(" a/s : Joint 2 BCK/FWD")
|
59
|
+
print(" z/x : Joint 3 BCK/FWD")
|
60
|
+
print(" e/r : Joint 4 BCK/FWD")
|
61
|
+
print(" d/f : Joint 5 L/R")
|
62
|
+
print(" c/v : Joint 6 L/R")
|
63
|
+
|
64
|
+
print("\nGripper Controls:")
|
65
|
+
print(" g/h : Open/Close gripper")
|
66
|
+
print(" b : Calibrate gripper")
|
67
|
+
|
68
|
+
print("\nOther Controls:")
|
69
|
+
print(" o/p : increase/decrease step size")
|
70
|
+
print(" -/+ : decrease/increase speed")
|
71
|
+
print("\nCTRL-C or 0 to quit")
|
72
|
+
|
73
|
+
# Reserve space for dynamic updates
|
74
|
+
print("\n" + "=" * 43 + "\r")
|
75
|
+
|
76
|
+
def update_display(self):
|
77
|
+
"""Update step size and speed in place without adding new lines"""
|
78
|
+
sys.stdout.write(f"\rCurrent step size: {self.step_size:.1f} | Current speed: {self.arm_speed}% ")
|
79
|
+
sys.stdout.flush() # Ensure immediate update
|
80
|
+
|
81
|
+
|
82
|
+
def get_arm_command(self):
|
83
|
+
key = None
|
84
|
+
# Read keyboard input
|
85
|
+
if self.kb.kbhit() is not None:
|
86
|
+
key = self.kb.getch()
|
87
|
+
|
88
|
+
while sys.stdin in select([sys.stdin], [], [], 0)[0]:
|
89
|
+
sys.stdin.read(1)
|
90
|
+
|
91
|
+
if key == self.last_key:
|
92
|
+
self.last_key = None
|
93
|
+
return None, None
|
94
|
+
|
95
|
+
self.last_key = key
|
96
|
+
|
97
|
+
# Check for quit conditions
|
98
|
+
if key == '0': # ESC or Ctrl-C
|
99
|
+
self.stop = True
|
100
|
+
return None, None
|
101
|
+
|
102
|
+
if key == 'o':
|
103
|
+
self.step_size += 0.1
|
104
|
+
elif key == 'p':
|
105
|
+
self.step_size -= 0.1
|
106
|
+
if key == '-':
|
107
|
+
self.arm_speed -= 10
|
108
|
+
if self.arm_speed < 0:
|
109
|
+
self.arm_speed = 0
|
110
|
+
elif key == '+':
|
111
|
+
self.arm_speed += 10
|
112
|
+
if self.arm_speed > 100:
|
113
|
+
self.arm_speed = 100
|
114
|
+
|
115
|
+
command, value = self.get_arm_value_from_key(key)
|
116
|
+
return command, value
|
117
|
+
else:
|
118
|
+
self.last_key = None
|
119
|
+
return None, None
|
120
|
+
|
121
|
+
def get_arm_value_from_key(self, key):
|
122
|
+
# Joint controls
|
123
|
+
if key == 'q': # Joint 1 left
|
124
|
+
self.j_agl_1 += self.step_size*100
|
125
|
+
return 1, self.j_agl_1
|
126
|
+
elif key == 'w': # Joint 1 right
|
127
|
+
self.j_agl_1 -= self.step_size*100
|
128
|
+
return 1, self.j_agl_1
|
129
|
+
elif key == 'a': # Joint 2 left
|
130
|
+
self.j_agl_2 += self.step_size*100
|
131
|
+
return 2, self.j_agl_2
|
132
|
+
elif key == 's': # Joint 2 right
|
133
|
+
self.j_agl_2 -= self.step_size*100
|
134
|
+
return 2, self.j_agl_2
|
135
|
+
elif key == 'z': # Joint 3 left
|
136
|
+
self.j_agl_3 += self.step_size*100
|
137
|
+
return 3, self.j_agl_3
|
138
|
+
elif key == 'x': # Joint 3 right
|
139
|
+
self.j_agl_3 -= self.step_size*100
|
140
|
+
return 3, self.j_agl_3
|
141
|
+
elif key == 'e': # Joint 4 left
|
142
|
+
self.j_agl_4 += self.step_size*100
|
143
|
+
return 4, self.j_agl_4
|
144
|
+
elif key == 'r': # Joint 4 right
|
145
|
+
self.j_agl_4 -= self.step_size*100
|
146
|
+
return 4, self.j_agl_4
|
147
|
+
elif key == 'd': # Joint 5 left
|
148
|
+
self.j_agl_5 += self.step_size*100
|
149
|
+
return 5, self.j_agl_5
|
150
|
+
elif key == 'f': # Joint 5 right
|
151
|
+
self.j_agl_5 -= self.step_size*100
|
152
|
+
return 5, self.j_agl_5
|
153
|
+
elif key == 'c': # Joint 6 left
|
154
|
+
self.j_agl_6 += self.step_size*100
|
155
|
+
return 6, self.j_agl_6
|
156
|
+
elif key == 'v': # Joint 6 right
|
157
|
+
self.j_agl_6 -= self.step_size*100
|
158
|
+
return 6, self.j_agl_6
|
159
|
+
# Gripper controls
|
160
|
+
elif key == 'g': # Open gripper
|
161
|
+
return 'gripper_open', 'True'
|
162
|
+
elif key == 'h': # Close gripper
|
163
|
+
return 'gripper_close', 'True'
|
164
|
+
elif key == 'b': # Calibrate gripper
|
165
|
+
return 'gripper_calibrate', 'True'
|
166
|
+
else:
|
167
|
+
return None, None
|
168
|
+
|
169
|
+
|
170
|
+
def run(self):
|
171
|
+
while not self.stop:
|
172
|
+
response = None
|
173
|
+
command, value = self.get_arm_command()
|
174
|
+
if command is not None:
|
175
|
+
if isinstance(command, int): # Joint movement
|
176
|
+
# Limit joint angles based on which joint
|
177
|
+
max_angle = 175.0 if command == 6 else 165.0
|
178
|
+
if abs(value) <= max_angle:
|
179
|
+
response = self.robot.arm.move_joint(command, value, self.arm_speed)
|
180
|
+
|
181
|
+
elif command == 'gripper_open':
|
182
|
+
response = self.robot.arm.gripper.open()
|
183
|
+
elif command == 'gripper_close':
|
184
|
+
response = self.robot.arm.gripper.close()
|
185
|
+
elif command == 'gripper_calibrate':
|
186
|
+
response = self.robot.arm.gripper.calibrate()
|
187
|
+
|
188
|
+
if response == False:
|
189
|
+
break
|
190
|
+
time.sleep(0.2)
|
191
|
+
self.update_display()
|
192
|
+
|
193
|
+
def cleanup(self):
|
194
|
+
"""Cleanup method to properly shut down the robot and restore terminal settings"""
|
195
|
+
try:
|
196
|
+
# Restore terminal settings
|
197
|
+
self.kb.set_normal_term()
|
198
|
+
# Dock the robot
|
199
|
+
# self.robot.dock()
|
200
|
+
self.robot.arm.move_joints(0,0,0,0,0,0,50)
|
201
|
+
time.sleep(2)
|
202
|
+
# Destroy the robot connection
|
203
|
+
self.robot.destroy()
|
204
|
+
|
205
|
+
except Exception as e:
|
206
|
+
print(f"\nError during cleanup: {e}")
|
207
|
+
# Try to restore terminal settings even if there's an error
|
208
|
+
try:
|
209
|
+
self.kb.set_normal_term()
|
210
|
+
except:
|
211
|
+
pass
|
212
|
+
|
213
|
+
def __del__(self):
|
214
|
+
"""Destructor to ensure cleanup is called"""
|
215
|
+
self.cleanup()
|
216
|
+
|
217
|
+
|
218
|
+
# Main entry point
|
219
|
+
if __name__ == '__main__':
|
220
|
+
teleop = None
|
221
|
+
try:
|
222
|
+
teleop = ArmTeleop()
|
223
|
+
teleop.run()
|
224
|
+
except KeyboardInterrupt:
|
225
|
+
print("\nShutting down...")
|
226
|
+
except Exception as e:
|
227
|
+
print(f"\nError: {e}")
|
228
|
+
finally:
|
229
|
+
if teleop:
|
230
|
+
teleop.cleanup()
|