Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# Images
*.png
*.ico

# IntelliJ project files
.idea
*.iml
out
gen

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down Expand Up @@ -127,3 +137,77 @@ dmypy.json

# Pyre type checker
.pyre/


### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml

# Gradle
.idea/**/gradle.xml
.idea/**/libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# mlf-sim
# mlf-sim

Simulador del robot MK2. Las imágenes utilizadas en el código se encuentran en <a href="https://drive.google.com/drive/folders/15hEw-D7YEgFCm1U_-46KibJb-yGSLQgf?usp=sharing">esta carpeta de Google Drive</a>.
31 changes: 31 additions & 0 deletions RepeatButton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import tkinter.ttk as ttk

'''
Wrapper for ttk.Button which repeatinterval and repeatdelay options.
Taken from https://stackoverflow.com/a/29824277
'''


class RepeatButton(ttk.Button):
def __init__(self, *args, **kwargs):

self.callback = kwargs.pop('command', None)
self.repeatinterval = kwargs.pop('repeatinterval', 100)
self.repeatdelay = kwargs.pop('repeatdelay', 300)

ttk.Button.__init__(self, *args, **kwargs)

if self.callback:
self.bind('<ButtonPress-1>', self.click)
self.bind('<ButtonRelease-1>', self.release)

def click(self, event=None):
self.callback()
self.after_id = self.after(self.repeatdelay, self.repeat)

def repeat(self):
self.callback()
self.after_id = self.after(self.repeatinterval, self.repeat)

def release(self, event=None):
self.after_cancel(self.after_id)
162 changes: 144 additions & 18 deletions tarea_2.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,54 @@
import numpy as np
from tkinter import *
from tkinter import ttk
from PIL import Image, ImageTk

import matplotlib.pyplot as plt
from matplotlib.pyplot import Slider, Button
from matplotlib.pyplot import Figure
from matplotlib.pyplot import Slider
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

from mk2robot import MK2Robot
from RepeatButton import RepeatButton

# Create main window
root = Tk()
root.iconbitmap("MLF.ico")
root.title("My Little Factory")

# A Frame that's going to contain the other widgets.
mainframe = ttk.Frame(root)
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))

root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)


""" 1. Useful functions """


def update(val):
# This function is called ny time a slider value changes
robot.update_pose(q0_slider.val, q1_slider.val, q2_slider.val)
[X_pos, Y_pos, Z_pos] = robot.get_joint_positions()
plot_robot(X_pos, Y_pos, Z_pos)
fig.canvas.draw_idle()


def plot_robot(X_pos, Y_pos, Z_pos):

# Clear figure
ax.clear()

# Plot the data
ax.scatter(0, 0, 0, zdir='z', s=30) # Origin
ax.plot([0,X_pos[0]],[0,Y_pos[0]],[0,Z_pos[0]]) # L0
ax.plot([X_pos[0],X_pos[1]],[Y_pos[0],Y_pos[1]],[Z_pos[0],Z_pos[1]]) # L1
ax.plot([X_pos[1],X_pos[2]],[Y_pos[1],Y_pos[2]],[Z_pos[1],Z_pos[2]]) # L2
ax.plot([X_pos[2],X_pos[3]],[Y_pos[2],Y_pos[3]],[Z_pos[2],Z_pos[3]]) # L3
ax.scatter(X_pos, Y_pos, Z_pos, zdir='z', s=20) # Joints
ax.scatter(0, 0, 0, zdir='z', s=30) # Origin
ax.plot([0, X_pos[0]], [0, Y_pos[0]], [0, Z_pos[0]]) # L0
ax.plot([X_pos[0], X_pos[1]], [Y_pos[0], Y_pos[1]], [Z_pos[0], Z_pos[1]]) # L1
ax.plot([X_pos[1], X_pos[2]], [Y_pos[1], Y_pos[2]], [Z_pos[1], Z_pos[2]]) # L2
ax.plot([X_pos[2], X_pos[3]], [Y_pos[2], Y_pos[3]], [Z_pos[2], Z_pos[3]]) # L3
ax.scatter(X_pos, Y_pos, Z_pos, zdir='z', s=20) # Joints

# Make it prettier
ax.set_title('MK2 Arm Simulator')

ax.set_ylabel('Y [mm]')
ax.set_xlabel('X [mm]')
ax.set_zlabel('Z [mm]')
Expand All @@ -38,29 +59,40 @@ def plot_robot(X_pos, Y_pos, Z_pos):
ax.set_zlim(0, 300)



""" 2. The actual script """

# Spawn a robot!
robot = MK2Robot(link_lengths=[55, 39, 135, 147, 66.3])
robot.update_pose(0, 0, 90)
[X_pos, Y_pos, Z_pos] = robot.get_joint_positions()

# Change the style of the plots.
plt.style.use('ggplot')

# Create the figure
fig = plt.figure()
fig = Figure()
ax = fig.add_subplot(111, projection='3d')
fig.set_facecolor(ax.get_facecolor())

# Plot the robot for the first time
plot_robot(X_pos, Y_pos, Z_pos)

axcolor = 'lightgoldenrodyellow'
ax.margins(x=0)

# Adjust the main plot to make room for the sliders
plt.subplots_adjust(bottom=0.4)
# Add to the mainframe
canvas = FigureCanvasTkAgg(fig, mainframe)
canvas.get_tk_widget().grid(row=0, column=0, columnspan=3)


""" 3. Sliders creation """

# Create a Figure for the sliders
fig_slider = Figure(figsize=(6, 1))
fig_slider.set_facecolor(ax.get_facecolor())

# Make horizontal sliders
axq0 = plt.axes([0.1, 0.1, 0.8, 0.03], facecolor=axcolor)
axq0 = fig_slider.add_axes([0.1, 0.1, 0.8, 0.08])
q0_slider = Slider(
ax=axq0,
label='q0 [º]',
Expand All @@ -69,7 +101,7 @@ def plot_robot(X_pos, Y_pos, Z_pos):
valinit=0,
)

axq1 = plt.axes([0.1, 0.2, 0.8, 0.03], facecolor=axcolor)
axq1 = fig_slider.add_axes([0.1, 0.45, 0.8, 0.08])
q1_slider = Slider(
ax=axq1,
label='q1 [º]',
Expand All @@ -78,7 +110,7 @@ def plot_robot(X_pos, Y_pos, Z_pos):
valinit=0,
)

axq2 = plt.axes([0.1, 0.3, 0.8, 0.03], facecolor=axcolor)
axq2 = fig_slider.add_axes([0.1, 0.8, 0.8, 0.08])
q2_slider = Slider(
ax=axq2,
label='q2 [º]',
Expand All @@ -92,8 +124,102 @@ def plot_robot(X_pos, Y_pos, Z_pos):
q1_slider.on_changed(update)
q2_slider.on_changed(update)

# Now we are ready to go
plt.show()
# Add the sliders to the mainframe
canvas2 = FigureCanvasTkAgg(fig_slider, mainframe)
canvas2.get_tk_widget().grid(row=1, column=2, rowspan=3, sticky=W)


""" 4. Buttons creation """


def slider_setter(slider):
def move_left_slider():
return slider.set_val(max(slider.val - 1, slider.valmin))

def move_right_slider():
return slider.set_val(min(slider.val + 1, slider.valmax))
return move_left_slider, move_right_slider


# Set the functions called from the Buttons.
left_q0, right_q0 = slider_setter(q0_slider)
left_q1, right_q1 = slider_setter(q1_slider)
left_q2, right_q2 = slider_setter(q2_slider)

# Icons made by Freepik from www.flaticon.com
left_img = Image.open("left.png")
left_img = left_img.resize((16, 16))
right_img = Image.open("right.png")
right_img = right_img.resize((16, 16))

left_img = ImageTk.PhotoImage(left_img)
right_img = ImageTk.PhotoImage(right_img)

# Make Buttons
left_q0_btn = RepeatButton(
mainframe,
image=left_img,
command=left_q0,
repeatinterval=100,
repeatdelay=10
)

right_q0_btn = RepeatButton(
mainframe,
image=right_img,
command=right_q0,
repeatinterval=100,
repeatdelay=10
)

left_q1_btn = RepeatButton(
mainframe,
image=left_img,
command=left_q1,
repeatinterval=100,
repeatdelay=10
)

right_q1_btn = RepeatButton(
mainframe,
image=right_img,
command=right_q1,
repeatinterval=100,
repeatdelay=10
)

left_q2_btn = RepeatButton(
mainframe,
image=left_img,
command=left_q2,
repeatinterval=100,
repeatdelay=10)

right_q2_btn = RepeatButton(
mainframe,
image=right_img,
command=right_q2,
repeatinterval=100,
repeatdelay=10
)

left_q0_btn.grid(row=3, column=0, sticky=E, padx=(20, 5), pady=5)
right_q0_btn.grid(row=3, column=1, sticky=W, padx=5, pady=5)
left_q1_btn.grid(row=2, column=0, sticky=E, padx=(20, 5), pady=5)
right_q1_btn.grid(row=2, column=1, sticky=W, padx=5, pady=5)
left_q2_btn.grid(row=1, column=0, sticky=E, padx=(20, 5), pady=5)
right_q2_btn.grid(row=1, column=1, sticky=W, padx=5, pady=5)


# Create an style to get the background of matplotlib plots.
background = fig.get_facecolor()
background = [int(background[i]*255) for i in range(3)]
background = [hex(background[i])[2:] for i in range(3)]
bg = f"#{background[0]}{background[1]}{background[2]}"

s = ttk.Style()
s.configure('.', background=bg)


# Start the loop to print on screen.
root.mainloop()