Skip to content
Snippets Groups Projects
Commit fe3fcde2 authored by Henrik Lindgren's avatar Henrik Lindgren
Browse files

add control panel and pose feature

parent 63d8dee3
No related branches found
No related tags found
No related merge requests found
...@@ -3,9 +3,15 @@ import json ...@@ -3,9 +3,15 @@ import json
import bpy import bpy
from bpy_extras.io_utils import ExportHelper from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty from bpy.props import StringProperty, IntProperty, PointerProperty
from bpy.types import Operator from bpy.types import Operator
import requests
DOFBOT_IP = '192.168.50.172'
DOFBOT_PORT = 5000
def print(data): def print(data):
for window in bpy.context.window_manager.windows: for window in bpy.context.window_manager.windows:
...@@ -33,17 +39,17 @@ def get_servo_objects(): ...@@ -33,17 +39,17 @@ def get_servo_objects():
def get_moving_angle(servo_name, rot_deg): def get_moving_angle(servo_name, rot_deg):
if servo_name == 'bottom_rotation': if servo_name == 'bottom_rotation':
return rot_deg.z return rot_deg.z + 90
if servo_name == 'joint_1': if servo_name == 'joint_1':
return rot_deg.y return rot_deg.y + 90
if servo_name == 'joint_2': if servo_name == 'joint_2':
return rot_deg.y return rot_deg.y + 90
if servo_name == 'joint_3': if servo_name == 'joint_3':
return rot_deg.y return rot_deg.y + 90
if servo_name == 'claw_rotation': if servo_name == 'claw_rotation':
return rot_deg.z return rot_deg.z + 90
if servo_name == 'claw_grip': if servo_name == 'claw_grip':
return rot_deg.x return rot_deg.x*2
def frame2time(frame): def frame2time(frame):
...@@ -58,9 +64,15 @@ class RotationDegrees: ...@@ -58,9 +64,15 @@ class RotationDegrees:
self.z = round(math.degrees(euler_rot.z)) self.z = round(math.degrees(euler_rot.z))
# This method of stepping through frame by frame and looking at the objects matrix_world def get_servo_angle(servo_object):
# was taken from the below export script by @astronotter rot_deg = RotationDegrees(servo_object.rotation_euler)
# https://gist.github.com/astronotter/faec735fc5e270df57741705052096ac angle = get_moving_angle(servo_object.name, rot_deg)
# print((frame, servo_object.name, angle, servo_object.rotation_euler))
return angle
def get_servo_data(): def get_servo_data():
frame_range = range(bpy.context.scene.frame_start, bpy.context.scene.frame_end + 1) frame_range = range(bpy.context.scene.frame_start, bpy.context.scene.frame_end + 1)
...@@ -68,21 +80,35 @@ def get_servo_data(): ...@@ -68,21 +80,35 @@ def get_servo_data():
previous_angle = None previous_angle = None
for frame in frame_range: for frame in frame_range:
bpy.context.scene.frame_set(frame) bpy.context.scene.frame_set(frame)
matrix = obj.matrix_world.copy() angle = get_servo_angle(obj)
# posx, posy, posz = matrix.to_translation()[:]
# scalex, scaley, scalez = matrix.to_scale()[:]
rot_deg = RotationDegrees(matrix.to_euler())
angle = get_moving_angle(obj.name, rot_deg)
if previous_angle == angle: if previous_angle == angle:
continue continue
previous_angle = angle previous_angle = angle
yield (obj.name, [frame2time(frame), angle]) yield (obj.name, [frame2time(frame), angle])
bpy.context.scene.frame_set(1)
def collect_servo_data_snapshot():
servos = {
'bottom_rotation': [],
'joint_1': [],
'joint_2': [],
'joint_3': [],
'claw_rotation': [],
'claw_grip': []
}
for obj in get_servo_objects():
angle = get_servo_angle(obj)
servos[obj.name].append([2000, angle])
return servos
def calc_servo_data(): def collect_servo_data():
servos = { servos = {
'bottom_rotation': [], 'bottom_rotation': [],
'joint_1': [], 'joint_1': [],
...@@ -98,10 +124,44 @@ def calc_servo_data(): ...@@ -98,10 +124,44 @@ def calc_servo_data():
return servos return servos
class DofbotProperties(bpy.types.PropertyGroup):
dofbot_ip: StringProperty(
name="IP",
description="The IP address of the Dofbot",
default=DOFBOT_IP,
)
dofbot_port: StringProperty(
name="Port",
description="The port of the Dofbot control API",
default=str(DOFBOT_PORT),
)
class DofbotRunAnimation(Operator):
"""Run animation on dofbot"""
bl_label = "Run animation"
bl_idname = "dofbot.run_animation"
def execute(self, context):
run_animation()
return {'FINISHED'}
class DofbotSetCurrentPose(Operator):
"""Set Dofbot to the current pose"""
bl_label = "Set pose"
bl_idname = "dofbot.set_current_pose"
def execute(self, context):
set_current_pose()
return {'FINISHED'}
class ExportDofbotAnimation(Operator, ExportHelper): class ExportDofbotAnimation(Operator, ExportHelper):
"""Exports Dofbot animation as json""" """Exports Dofbot animation as json"""
bl_idname = "export_dofbot.json_animation" # important since its how bpy.ops.import_test.some_data is constructed bl_idname = 'dofbot.export_json_animation' # important since its how bpy.ops.import_test.some_data is constructed
bl_label = "Export Dofbot Json Animation" bl_label = '' # "Export Dofbot Json Animation"
# ExportHelper mixin class uses this # ExportHelper mixin class uses this
filename_ext = ".json" filename_ext = ".json"
...@@ -113,31 +173,108 @@ class ExportDofbotAnimation(Operator, ExportHelper): ...@@ -113,31 +173,108 @@ class ExportDofbotAnimation(Operator, ExportHelper):
) )
def execute(self, context): def execute(self, context):
servo_data = calc_servo_data() servo_data = collect_servo_data()
with open(self.filepath, 'w') as f: with open(self.filepath, 'w') as f:
f.write(json.dumps(servo_data)) f.write(json.dumps(servo_data))
return {'FINISHED'} return {'FINISHED'}
# Only needed if you want to add into a dynamic menu class ExportDofbotPose(Operator, ExportHelper):
def menu_func_export(self, context): """Exports Dofbot pose as json"""
self.layout.operator(ExportDofbotAnimation.bl_idname, text="Dofbot Animation Export") bl_idname = 'dofbot.export_json_pose' # important since its how bpy.ops.import_test.some_data is constructed
bl_label = '' # "Export Dofbot Json Pose"
# ExportHelper mixin class uses this
filename_ext = ".json"
filter_glob: StringProperty(
default="*.json",
options={'HIDDEN'},
maxlen=255, # Max internal buffer length, longer would be clamped.
)
def execute(self, context):
servo_data = collect_servo_data_snapshot()
with open(self.filepath, 'w') as f:
f.write(json.dumps(servo_data))
return {'FINISHED'}
class DofbotPanel(bpy.types.Panel):
"""Open Dofbot Control Panel"""
bl_label = 'Dofbot Control'
bl_idname = 'wm.dofbot_panel'
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Dofbot Control'
def draw(self, context):
layout = self.layout
dofbot = context.scene.dofbot
row = layout.row()
row.prop(dofbot, 'dofbot_ip')
row = layout.row()
row.prop(dofbot, 'dofbot_port')
row = layout.row()
row.operator('dofbot.run_animation', icon='PLAY')
row.operator('dofbot.export_json_animation', icon='EXPORT')
row = layout.row()
row.operator('dofbot.set_current_pose', icon='FF')
row.operator('dofbot.export_json_pose', icon='EXPORT')
# Register and add to the "file selector" menu (required to use F3 search "Text Export Operator" for quick access). # Register and add to the "file selector" menu (required to use F3 search "Text Export Operator" for quick access).
def register(): def register():
bpy.utils.register_class(DofbotProperties)
bpy.types.Scene.dofbot = PointerProperty(type=DofbotProperties)
bpy.utils.register_class(DofbotPanel)
bpy.utils.register_class(DofbotRunAnimation)
bpy.utils.register_class(DofbotSetCurrentPose)
bpy.utils.register_class(ExportDofbotAnimation) bpy.utils.register_class(ExportDofbotAnimation)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export) bpy.utils.register_class(ExportDofbotPose)
def unregister(): def unregister():
bpy.utils.unregister_class(DofbotProperties)
del bpy.types.scene.dofbot
bpy.utils.unregister_class(DofbotPanel)
bpy.utils.unregister_class(DofbotRunAnimation)
bpy.utils.unregister_class(DofbotSetCurrentPose)
bpy.utils.unregister_class(ExportDofbotAnimation) bpy.utils.unregister_class(ExportDofbotAnimation)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) bpy.utils.unregister_class(ExportDofbotPose)
def send_motion(servo_data):
requests.post('http://' + DOFBOT_IP + ':' + str(DOFBOT_PORT) + '/motion', json = servo_data)
def run_animation():
servo_data = collect_servo_data()
send_motion(servo_data)
def set_current_pose():
servo_data = collect_servo_data_snapshot()
send_motion(servo_data)
def export_animation():
bpy.ops.export_dofbot.json_animation('INVOKE_DEFAULT')
if __name__ == "__main__": if __name__ == "__main__":
register() register()
# test call # # test call
bpy.ops.export_dofbot.json_animation('INVOKE_DEFAULT') # bpy.ops.export_dofbot.json_animation('INVOKE_DEFAULT')
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment