Initial commit

This commit is contained in:
Johannes Schriewer 2017-02-05 00:48:50 +01:00
commit 6f5c17489c
12 changed files with 305 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.pyc
__pycache__

78
Arduino/glotz.ino Normal file
View file

@ -0,0 +1,78 @@
#include <Servo.h>
// Adjust these for your servos
const int hMin = 1600;
const int hMax = 700;
const int vMin = 1100;
const int vMax = 1600;
// Do not change anything after this if you don't know what you're doing
Servo horizontal;
Servo vertical;
// if override is set to -1, the control box takes over
int override_h = -1;
int override_v = -1;
void setup() {
// horizontal swiveling servo is on pin 10
horizontal.attach(10);
// vertical servo on 9
vertical.attach(9);
// start the serial processing
Serial.begin(9600);
}
void loop() {
// Horizontal potientiometer is on A1
int h = analogRead(A1);
// Vertival potentiometer is on A0
int v = analogRead(A0);
// if the override is active replace measurement
if (override_h >= 0){
h = override_h;
}
if (override_v >= 0){
v = override_v;
}
// map ADC converted value into safe range
h = map(black, 0, 1023, hMin, hMax);
v = map(red, 0, 1023, vMin, vMax);
// set servos
horizontal.writeMicroseconds(h);
vertical.writeMicroseconds(v);
// wait some time to allow the PWM to settle
delay(5);
}
void serialEvent() {
// While there are bytes in the serial queue process them
while (Serial.available()) {
// read two comma separated values
int h = Serial.parseInt();
int v = Serial.parseInt();
// if the next char is a newline the format was correct
if (Serial.read() == '\n') {
// set overrides
override_h = constrain(h, -1, 1023);
override_v = constrain(v, -1, 1023);
// echo back what has been set
Serial.print("Setting values: h = ");
Serial.print(override_h);
Serial.print(", v = ");
Serial.println(override_v);
}
}
}

48
Readme.md Normal file
View file

@ -0,0 +1,48 @@
# Klein Glotzi
Simple Arduino based two axis camera gimbal for fitting a Microsoft LifeCam.
# Arduino sketch
You have to try out the calibration settings for the servos you're using, just modify the min and max settings in the sketch.
## Panning with the control box
There's a control-box attached to the arduino with just two potentiometers for controlling each of the axes.
## Panning via serial port
You can use the serial port in 9600,8,N,1 mode to send a string of two comma separated integers to control the panning via software.
Just send something like this: `512,512\n` to center the camera, minimum value is zero, maximum is 1023.
Sending a positioning coordinate disables the control box. Send a `-1` for the axis you want to re-enable
# Printing the STLs
You can use the `All_Parts_Plate.stl` file to print all parts at once or just print all things one by one.
The base box has no bottom and no lid as printing such big monotonous forms will most likely deform while printing. Just use a sheet of acrylic or some aluminium to make the top and bottom plates.
Currently the screwing holes for the Arduino are somewhat off, I will fix that in the future. To route the cables for the top servo and the control box i just drilled a hole in the back.
# Python GUI
There is a simple python GUI to control Klein Glotzi from a Linux computer. You'll need GTK 3, the python gi and serial modules.
Install them from your distro repos or create a virtualenv and install the requirements:
```bash
cd glotzi
pyvenv ~/.virtualenvs/glotzi
. ~/.virtualenvs/glotzi/bin/activate
cd pygui
python main.py
```
Just run `main.py` and a window should show up. When selecting a serial port the GUI immediately takes over control and centers the camera. If you close the window control is returned to the control-box.
# TODO
- Wiring schematic
- Fix Arduino screwing holes
- Add cable slot for control-box and top servo

BIN
STL/All_Parts_Plate.stl Normal file

Binary file not shown.

BIN
STL/Box.stl Normal file

Binary file not shown.

BIN
STL/Cam_Bracket.stl Normal file

Binary file not shown.

BIN
STL/Gimbal_Bracket.stl Normal file

Binary file not shown.

BIN
STL/Servo_Bracket.stl Normal file

Binary file not shown.

0
pygui/gui/__init__.py Normal file
View file

120
pygui/gui/control_window.py Normal file
View file

@ -0,0 +1,120 @@
import platform
import threading
from datetime import datetime
from time import sleep
import gi
from serial.tools import list_ports
import serial
# we need Gtk 3.0
gi.require_version('Gtk', '3.0')
# import everything we need for a Gtk Window
from gi.repository import Gtk, Gio, Gdk, GObject
# Control window
class ControlWindow(Gtk.Window):
def __init__(self):
self.connection = None
self.old_h = -1
self.old_v = -1
# initialize window
Gtk.Window.__init__(self, title="Klein Glotzi")
# add header bar
self.header = Gtk.HeaderBar()
self.header.set_show_close_button(True)
self.header.props.title = "Klein Glotzi"
self.set_titlebar(self.header)
# video source buttons
self.box = Gtk.Box(spacing=10, orientation=Gtk.Orientation.VERTICAL)
self.add(self.box)
# device drop down
type_store = Gtk.ListStore(str, str)
port_combobox = Gtk.ComboBox.new_with_model(type_store)
renderer_text = Gtk.CellRendererText()
port_combobox.pack_start(renderer_text, True)
port_combobox.add_attribute(renderer_text, "text", 0)
port_combobox.connect('changed', self.on_port_changed)
port_combobox.set_vexpand(False)
for port in list_ports.grep('2341:0043'):
type_store.append([port.name, port.device])
self.box.pack_start(port_combobox, True, True, 0)
# sliders
h_label = Gtk.Label("Horizontal")
self.box.pack_start(h_label, True, True, 0)
self.h_scale = Gtk.Scale()
self.h_scale.set_orientation(Gtk.Orientation.HORIZONTAL)
self.h_scale.set_range(0,1023)
self.h_scale.set_value(512)
self.h_scale.set_draw_value(False)
self.box.pack_start(self.h_scale, True, True, 0)
v_label = Gtk.Label("Vertical")
self.box.pack_start(v_label, True, True, 0)
self.v_scale = Gtk.Scale()
self.v_scale.set_orientation(Gtk.Orientation.HORIZONTAL)
self.v_scale.set_range(0,1023)
self.v_scale.set_value(512)
self.v_scale.set_draw_value(False)
self.box.pack_start(self.v_scale, True, True, 0)
# no border
self.set_border_width(10)
# show all window elements
self.show_all()
# resize the window and disable resizing by user if needed
self.set_resizable(False)
# on quit run callback to stop pipeline
self.connect("delete-event", self.quit)
def __del__(self):
self.quit()
def on_port_changed(self, combobox):
index = combobox.get_active()
if index is not None:
model = combobox.get_model()
entry = list(model[index])
if self.connection:
connection.close()
self.connection = serial.Serial(entry[-1], 9600)
GObject.timeout_add(100, self.on_timeout, None)
def on_timeout(self, context):
if self.connection:
h = int(self.h_scale.get_value())
v = int(self.v_scale.get_value())
if v != self.old_v or h != self.old_h:
self.connection.write(
'{},{}\n'.format(1023 - h,v).encode('ASCII')
)
self.old_h = h
self.old_v = v
return True
else:
return False
def quit(self, sender, gparam):
if self.connection:
self.connection.write(
'{},{}\n'.format(-1, -1).encode('ASCII')
)
Gtk.main_quit()

55
pygui/main.py Normal file
View file

@ -0,0 +1,55 @@
import signal
# gi is GObject instrospection
import gi
# we need GStreamer 1.0 and Gtk 3.0
gi.require_version('Gst', '1.0')
gi.require_version('Gtk', '3.0')
# import everything we need for a Gtk Window
from gi.repository import Gtk, GObject, GLib
from gui.control_window import ControlWindow
# Signal handler to allow HUP, INT and TERM signal handling
def InitSignal(gui):
def signal_action(signal):
if signal is 1:
print("Caught signal SIGHUP(1)")
return
elif signal is 2:
print("Caught signal SIGINT(2)")
elif signal is 15:
print("Caught signal SIGTERM(15)")
gui.quit(None, None)
def idle_handler(*args):
GLib.idle_add(signal_action, priority=GLib.PRIORITY_HIGH)
def handler(*args):
signal_action(args[0])
def install_glib_handler(sig):
unix_signal_add = None
if hasattr(GLib, "unix_signal_add"):
unix_signal_add = GLib.unix_signal_add
elif hasattr(GLib, "unix_signal_add_full"):
unix_signal_add = GLib.unix_signal_add_full
if unix_signal_add:
unix_signal_add(GLib.PRIORITY_HIGH, sig, handler, sig)
else:
print("Can't install GLib signal handler, too old gi.")
SIGS = [getattr(signal, s, None) for s in "SIGINT SIGTERM SIGHUP".split()]
for sig in filter(None, SIGS):
signal.signal(sig, idle_handler)
GLib.idle_add(install_glib_handler, sig, priority=GLib.PRIORITY_HIGH)
if __name__ == "__main__":
GObject.threads_init()
InitSignal(ControlWindow())
Gtk.main()

2
pygui/requirements.txt Normal file
View file

@ -0,0 +1,2 @@
gi
pyserial