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
import bpy
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty
from bpy.props import StringProperty, IntProperty, PointerProperty
from bpy.types import Operator
import requests
DOFBOT_IP = '192.168.50.172'
DOFBOT_PORT = 5000
def print(data):
for window in bpy.context.window_manager.windows:
......@@ -33,17 +39,17 @@ def get_servo_objects():
def get_moving_angle(servo_name, rot_deg):
if servo_name == 'bottom_rotation':
return rot_deg.z
return rot_deg.z + 90
if servo_name == 'joint_1':
return rot_deg.y
return rot_deg.y + 90
if servo_name == 'joint_2':
return rot_deg.y
return rot_deg.y + 90
if servo_name == 'joint_3':
return rot_deg.y
return rot_deg.y + 90
if servo_name == 'claw_rotation':
return rot_deg.z
return rot_deg.z + 90
if servo_name == 'claw_grip':
return rot_deg.x
return rot_deg.x*2
def frame2time(frame):
......@@ -58,9 +64,15 @@ class RotationDegrees:
self.z = round(math.degrees(euler_rot.z))
# This method of stepping through frame by frame and looking at the objects matrix_world
# was taken from the below export script by @astronotter
# https://gist.github.com/astronotter/faec735fc5e270df57741705052096ac
def get_servo_angle(servo_object):
rot_deg = RotationDegrees(servo_object.rotation_euler)
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():
frame_range = range(bpy.context.scene.frame_start, bpy.context.scene.frame_end + 1)
......@@ -68,21 +80,35 @@ def get_servo_data():
previous_angle = None
for frame in frame_range:
bpy.context.scene.frame_set(frame)
matrix = obj.matrix_world.copy()
# 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)
angle = get_servo_angle(obj)
if previous_angle == angle:
continue
previous_angle = 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 = {
'bottom_rotation': [],
'joint_1': [],
......@@ -98,10 +124,44 @@ def calc_servo_data():
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):
"""Exports Dofbot animation as json"""
bl_idname = "export_dofbot.json_animation" # important since its how bpy.ops.import_test.some_data is constructed
bl_label = "Export Dofbot Json Animation"
bl_idname = 'dofbot.export_json_animation' # important since its how bpy.ops.import_test.some_data is constructed
bl_label = '' # "Export Dofbot Json Animation"
# ExportHelper mixin class uses this
filename_ext = ".json"
......@@ -113,31 +173,108 @@ class ExportDofbotAnimation(Operator, ExportHelper):
)
def execute(self, context):
servo_data = calc_servo_data()
servo_data = collect_servo_data()
with open(self.filepath, 'w') as f:
f.write(json.dumps(servo_data))
return {'FINISHED'}
# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
self.layout.operator(ExportDofbotAnimation.bl_idname, text="Dofbot Animation Export")
class ExportDofbotPose(Operator, ExportHelper):
"""Exports Dofbot pose as json"""
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).
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.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.utils.register_class(ExportDofbotPose)
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.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__":
register()
# test call
bpy.ops.export_dofbot.json_animation('INVOKE_DEFAULT')
# # test call
# 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