Image-stitching and virtual tour solutions My account Updates
It is currently Sun Sep 21, 2014 3:13 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 39 posts ]  Go to page Previous  1, 2
Author Message
 Post subject:
PostPosted: Mon Mar 15, 2010 6:06 pm 
Offline
Member

Joined: Mon May 22, 2006 12:18 am
Posts: 7734
Location: Duesseldorf, Germany
olihar wrote:
Here is the motion curve from it made in Nuke.

First time i see Nuke used for that . . . :cool:;)

best, Klaus

_________________
Simplicity is the keynote of all true elegance. Coco Chanel


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 15, 2010 6:16 pm 
Offline
Member
User avatar

Joined: Sun Nov 25, 2007 11:12 pm
Posts: 1341
Location: Mont-Saint-André (Belgium)
OK Frederic, I will try ....:/
Quote:
You already have all the low-level parts (just use the code from Papywizard; I can provide a version without any dependencies to Papywizard)

Could-you provide this "version" to me ?:rolleyes:
Thanks

_________________
:cool: Claude :cool:
Merlin + Papywizard on Windows 7 & Nokia 770 § N810 & Acer (Netbook) + PanoramaApp Androïd + Deltawave PapyMerlin BT + Autopano
Spherical Pano (180 x 360) with Canon 40D + Canon EF-S 10-22mm f/3.5-4.5 Zoom & Pôle Pano with Canon 5D MK2 and shaved Tokina 10-17 3.5-4.5 AF DX Fisheye
Gigapixel photography with Nikon D200 + Sigma 70-200 F 2.8 EX DG APO HSM


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 15, 2010 6:50 pm 
Offline
Member
User avatar

Joined: Wed Dec 07, 2005 6:21 pm
Posts: 5826
Location: Grenoble, France
Here it is:

Code:
# -*- coding: utf-8 -*-

""" Panohead remote control.

License
=======

 - B{Papywizard} (U{http://www.papywizard.org}) is Copyright:
  - (C) 2007-2010 Frédéric Mantegazza

This software is governed by the B{CeCILL} license under French law and
abiding by the rules of distribution of free software.  You can  use,
modify and/or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
U{http://www.cecill.info}.

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability.

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and,  more generally, to use and operate it in the
same conditions as regards security.

The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.

Module purpose
==============

Hardware

Implements
==========

 - HardwareError
 - BluetoothDriver
 - MerlinOrionHardware

@author: Frédéric Mantegazza
@copyright: (C) 2007-2010 Frédéric Mantegazza
@license: CeCILL
"""

__revision__ = "0.2"

import time
import bluetooth

ENCODER_ZERO = 0x800000
BT_ADDRESS = "00:50:C2:58:56:6B"
TIMEOUT = 1.
CONNECT_DELAY = 8.
YAW_AXIS = 1
PITCH_AXIS = 2
SHUTTER_AXIS = 1


class HardwareError(Exception):
    pass


class BluetoothDriver(object):
    """ Driver for bluetooth connection.
    """
    def __init__(self, address=BT_ADDRESS):
        """ Init the bluetooth driver object.
        """
        super(BluetoothDriver, self).__init__()

        print "BluetoothDriver._init(): trying to connect to %s..." % address
        try:
            self.__sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
            self.__sock.connect((address, 1))
            try:
                self.__sock.settimeout(TIMEOUT)
            except NotImplementedError:
                print "BluetoothDriver._init(): bluetooth stack does not implment settimeout()"
        except bluetooth.BluetoothError, error:
            err, msg = eval(error.message)
            raise HardwareError(msg)
        else:
            time.sleep(CONNECT_DELAY)
            print "BluetoothDriver._init(): successfully connected to %s" % address

    def shutdown(self):
        self.__sock.close()

    def write(self, data):
        #print "BluetoothDriver.write(): data=%s" % repr(data)
        size = self.__sock.send(data)
        return size

    def read(self, size):
        data = self.__sock.recv(size)
        #print "BluetoothDriver.read(): data=%s" % repr(data)
        if size and not data:
            raise IOError("Timeout while reading on bluetooth bus (size=%d, data=%s)" % (size, repr(data)))
        else:
            return data


class MerlinOrionHardware(object):
    """ Merlin/Orion low-level hardware.
    """
    def __init__(self, axis, driver):
        """ Init the MerlinOrionHardware object.
        """
        super(MerlinOrionHardware, self).__init__()

        self.__axis = axis
        self.__driver = driver
        self.__encoderFullCircle = None

    def __decodeAxisValue(self, strValue):
        """ Decode value from axis.

        Values (position, speed...) returned by axis are
        32bits-encoded strings, low byte first.

        @param strValue: value returned by axis
        @type strValue: str

        @return: value
        @rtype: int
        """
        value = 0
        for i in xrange(3):
            value += eval("0x%s" % strValue[i*2:i*2+2]) * 2 ** (i * 8)
        return value

    def __encodeAxisValue(self, value):
        """ Encode value for axis.

        Values (position, speed...) to send to axis must be
        32bits-encoded strings, low byte first.

        @param value: value
        @type value: int

        @return: value to send to axis
        @rtype: str
        """
        strHexValue = "000000%s" % hex(value)[2:]
        strValue = strHexValue[-2:] + strHexValue[-4:-2] + strHexValue[-6:-4]
        return strValue.upper()

    def __encoderToAngle(self, codPos):
        """ Convert encoder value to degres.

        @param codPos: encoder position
        @type codPos: int

        @return: position, in °
        @rtype: float
        """
        return (codPos - ENCODER_ZERO) * 360. / self.__encoderFullCircle

    def __angleToEncoder(self, pos):
        """ Convert degres to encoder value.

        @param pos: position, in °
        @type pos: float

        @return: encoder position
        @rtype: int
        """
        return int(pos * self.__encoderFullCircle / 360. + ENCODER_ZERO)

    def __sendCmd(self, cmd, param=""):
        """ Send a command to the axis.

        @param cmd: command to send
        @type cmd: str

        @return: answer
        @rtype: str
        """
        cmd = "%s%d%s" % (cmd, self.__axis, param)
        #print "MerlinOrionHardware.__sendCmd(): axis %d cmd=%s" % (self.__axis, repr(cmd))
        for nbTry in range(3):
            try:
                answer = ""
                self.__driver.write(":%s\r" % cmd)
                c = ''
                while c not in ('=', '!'):
                    c = self.__driver.read(1)
                    #Lprint "MerlinOrionHardware.__sendCmd(): c=%s" % repr(c)
                if c == '!':
                    c = self.__driver.read(1) # Get error code
                    raise IOError("Error in command %s (err=%s)" % (repr(cmd), c))
                answer = ""
                while True:
                    c = self.__driver.read(1)
                    #print "MerlinOrionHardware.__sendCmd(): c=%s" % repr(c)
                    if c == '\r':
                        break
                    answer += c

            except IOError:
                 print "MerlinOrionHardware.__sendCmd(): axis %d can't sent command %s. Retrying..." % (self.__axis, repr(cmd))
            else:
                break
        else:
            raise HardwareError("axis %d can't send command %s" % (self.__axis, repr(cmd)))
        #print "MerlinOrionHardware.__sendCmd(): axis %d ans=%s" % (self.__axis, repr(answer))

        return answer

    def init(self):
        """ Init the MerlinOrion hardware.

        Done only once per axis.
        """

        # Stop motor
        self.__sendCmd("L")

        # Check motor?
        self.__sendCmd("F")

        # Get firmeware version
        value = self.__sendCmd("e")
        print "MerlinOrionHardware.init(): firmeware version=%s" % value

        # Get encoder full circle
        value = self.__sendCmd("a")
        self.__encoderFullCircle = self.__decodeAxisValue(value)
        print "MerlinOrionHardware.init(): encoder full circle=%s" % hex(self.__encoderFullCircle)

        # Get sidereal rate
        value = self.__sendCmd("D")
        print "MerlinOrionHardware.init(): sidereal rate=%s" % hex(self.__decodeAxisValue(value))

    def read(self):
        """ Read the axis position.

        @return: axis position, in °
        @rtype: float
        """
        value = self.__sendCmd("j")
        self.__driver.releaseBus()
        pos = self.__encoderToAngle(self.__decodeAxisValue(value))
        return pos

    def drive(self, pos):
        """ Drive the axis.

        @param pos: position to reach, in °
        @type pos: float
        """
        strValue = self.__encodeAxisValue(self.__angleToEncoder(pos))
        self.__sendCmd("L")
        self.__sendCmd("G", "00")
        self.__sendCmd("S", strValue)
        self.__sendCmd("J")

    def stop(self):
        """ Stop the axis.
        """
        self.__sendCmd("L")

    def startJog(self, dir_, speed):
        """ Start the axis.

        @param dir_: direction ('+', '-')
        @type dir_: str

        @param speed: speed
        @type speed: int
        """
        self.__sendCmd("L")
        if dir_ == '+':
            self.__sendCmd("G", "30")
        elif dir_ == '-':
            self.__sendCmd("G", "31")
        else:
            raise ValueError("Axis %d dir. must be in ('+', '-')" % self.__axis)
        self.__sendCmd("I", self.__encodeAxisValue(speed))
        self.__sendCmd("J")

    def getStatus(self):
        """ Get axis status.

        @return: axis status
        @rtype: str
        """
        return self.__sendCmd("f")

    def setOutput(self, state):
        """ Set output state.

        @param state: new state of the the output
        @type state: bool
        """
        self.__sendCmd("O", str(int(state)))


def test():
    """ Test.
    """
    driver = BluetoothDriver("00:50:C2:58:56:6B")
    yaw = MerlinOrionHardware(YAW_AXIS, driver)
    pitch = MerlinOrionHardware(PITCH_AXIS, driver)
    shutter = MerlinOrionHardware(SHUTTER_AXIS, driver)

    yaw.init()
    pitch.init()

    yaw.drive(10.)
    pitch.drive(-5.)
    while yaw.getStatus()[1] != '0':
        time.sleep(0.1)
    while pitch.getStatus()[1] != '0':
        time.sleep(0.1)
    shutter.setOutput(1)
    time.sleep(0.5)
    shutter.setOutput(0)

    driver.shutdown()


if __name__ == "__main__":
    """ test() won't be executed if the module is imported.
    """
    test()

You will need to install python, and bluetooth module...

This code is not thread-safe; this mean you can't drive the head from different threads, but as you can see in the test() function, it is possible to drive both axis at the same time.

_________________
Frédéric

Canon 20D + 17-40/f4 L USM + 70-200/f4 L USM + 50/f1.4 USM
Merlin/Orion panohead + Papywizard on Nokia N800 and HP TC-1100


Last edited by fma38 on Mon Mar 15, 2010 9:56 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 16, 2010 1:21 am 
Offline
New member

Joined: Tue Jul 14, 2009 6:02 pm
Posts: 2
Thanks a lot Frederic. This is perfect to start experimenting.
One quick question to which I couldn't find the answer yet in your script. How can we change the speed of the movement? What is the easiest way to do it?

...mike


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 16, 2010 7:44 am 
Offline
Member
User avatar

Joined: Wed Dec 07, 2005 6:21 pm
Posts: 5826
Location: Grenoble, France
You can't change the speed using the drive command; only the startJog can do that. Have a look at the MerlinOrionPlugins class to see how to use the low-level hardware class, especially the MANUAL_SPEED_TABLE:

http://www.papywizard.org/browser/trunk/papywizard/plugins/merlinOrionPlugins.py#L92

The speed param is the value of the divider used by the controller. Also carefully read the Merlin protocol:

http://www.papywizard.org/wiki/DevelopGuide

_________________
Frédéric

Canon 20D + 17-40/f4 L USM + 70-200/f4 L USM + 50/f1.4 USM
Merlin/Orion panohead + Papywizard on Nokia N800 and HP TC-1100


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 16, 2010 8:55 am 
Offline
Member
User avatar

Joined: Wed Dec 07, 2005 6:21 pm
Posts: 5826
Location: Grenoble, France
I realized that I didn't implement the low speeds; the code will need to be modified a little bit in the startJog() method. The 'G' command determines the global ratio: I always use 3. If you want to use lower speeds, you will need to use 1.

For more informations, have a look here:

http://sites.google.com/site/rwgastro/SupaTrakMountHacking.pdf

_________________
Frédéric

Canon 20D + 17-40/f4 L USM + 70-200/f4 L USM + 50/f1.4 USM
Merlin/Orion panohead + Papywizard on Nokia N800 and HP TC-1100


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 08, 2010 6:22 am 
Offline
Member

Joined: Tue Mar 10, 2009 1:43 am
Posts: 146
Location: Colorado, USA
Frederic,

Thanks for giving the sample code. I have updated the code with Serial driver to use it with my wired connection.

All,
I also implemented a Bezier curve to make the head go in the curved path. The new code is

Code:
# -*- coding: utf-8 -*-

""" Panohead remote control.

License
=======

 - B{Papywizard} (U{http://www.papywizard.org}) is Copyright:
  - (C) 2007-2010 Frédéric Mantegazza

This software is governed by the B{CeCILL} license under French law and
abiding by the rules of distribution of free software.  You can  use,
modify and/or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
U{http://www.cecill.info}.

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability.

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and,  more generally, to use and operate it in the
same conditions as regards security.

The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.

Module purpose
==============

Hardware

Implements
==========

 - HardwareError
 - BluetoothDriver
 - MerlinOrionHardware

@author: Frédéric Mantegazza
@author: Jones Henry Subbiah
@copyright: (C) 2007-2010 Frédéric Mantegazza
@license: CeCILL
"""

__revision__ = "$Id: merlinOrionHardware.py 2280 2010-02-13 12:27:30Z fma $"

import time
import serial
import math
#import bluetooth

ENCODER_ZERO = 0x800000
BT_ADDRESS = "00:50:C2:58:56:6B"
TIMEOUT = 1.
CONNECT_DELAY = 8.
YAW_AXIS = 1
PITCH_AXIS = 2
SHUTTER_AXIS = 1
SERIAL_PORT = "COM4"
SERIAL_BAUDRATE = 9600

class Point:
    """ Point represent the Bezier control points
       x represents yaw and y represents pitch
    """
    def __init__(self, x, y):
        self.x, self.y = float(x), float(y)

    def pprint(self):
        print "(%0.4f, %0.4f)" % (self.x, self.y)

class BezierCurve(object):
    """ class to generate Bezier curve
    """
    def __init__(self, points):
        self._points, self._steps = points, 30
    #
    # Gets input points for a Bezier curve
    # Returns a list of points equals the number of steps the
    # head stops and takes photos.
    #
    def bezier(self):
        outlist = []
        npoints = len(self._points)
        for j in range(self._steps):
            #print "j: %d" % j
            # the increment is always between 0.0 to 1.0
            inc = 1.0 * j / self._steps
            x, y = 0.0, 0.0

            # for each Bezier control points
            for i in range(npoints):
                poly = self.bpoly(npoints-1, i, inc)
                #print "i: %d, inc: %.04f, poly: %.04f" % (i, inc, poly)
                x += self._points[i].x * poly
                y += self._points[i].y * poly

            #print "x: %.04f, y: %.04f" % (x,y)
            outlist.append(Point(x,y))

        # add the last control point. So a step of 300 will result 301 shots
        # not neccessary - just for completeness
        outlist.append(Point(self._points[npoints-1].x,self._points[npoints-1].y))
        return outlist

    # this function calculate optimum number of steps to travel the curve
    # The fov is equal to horizontal FOV of lens used divided by 210.
    # 210 is derived as 7 secs of 30 fps video.
    # update the FOV field. The current one is for 150mm lens on a 1.6 crop camera
    # divided by 210
    # It starts at 30 steps, finds the  curve points, finds the difference of the first
    # 2 points. If it is less than the FOV/210, then that is the minimal steps needed.
    def findsteps(self):
        fov = 0.0410
        steps = 0
        l = []

        while True:
            steps += 30
            self._steps = steps
            l = self.bezier()
            print "steps: %d, l[1].x: %f, l[0].x: %f, diff: %f" % (steps, l[1].x, l[0].x, math.fabs(l[1].x - l[0].x))

            if math.fabs(l[1].x - l[0].x) < fov:
                break
        print "steps: %d" % steps

    # finds the polynomical for a given position
    def bpoly(self, order, position, inc):
        """ Bezier polynomial term
        """
        return self.bcoeff(order, position) * math.pow(inc, position) * math.pow ( 1.0 - inc, (order - position ) )

    # find the coefficient for a given position
    def bcoeff(self, order, position):
        """ Bezier coefficient
        """
        return math.factorial( order ) / ( math.factorial( position ) * math.factorial( order - position ) )

class HardwareError(Exception):
    pass

class BluetoothDriver(object):
    """ Driver for bluetooth connection.
    """
    def __init__(self, address=BT_ADDRESS):
        """ Init the bluetooth driver object.
        """
        super(BluetoothDriver, self).__init__()

        print "BluetoothDriver._init(): trying to connect to %s..." % address
        try:
            self.__sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
            self.__sock.connect((address, 1))
            try:
                self.__sock.settimeout(TIMEOUT)
            except NotImplementedError:
                print "BluetoothDriver._init(): bluetooth stack does not implment settimeout()"
        except bluetooth.BluetoothError, error:
            err, msg = eval(error.message)
            raise HardwareError(msg)
        else:
            time.sleep(CONNECT_DELAY)
            print "BluetoothDriver._init(): successfully connected to %s" % address

    def shutdown(self):
        self.__sock.close()

    def write(self, data):
        #print "BluetoothDriver.write(): data=%s" % repr(data)
        size = self.__sock.send(data)
        return size

    def read(self, size):
        data = self.__sock.recv(size)
        #print "BluetoothDriver.read(): data=%s" % repr(data)
        if size and not data:
            raise IOError("Timeout while reading on bluetooth bus (size=%d, data=%s)" % (size, repr(data)))
        else:
            return data

class SerialDriver(object):
    """ Driver for serial connection.
    """
    def __init__(self):
        try:
            self._serial = serial.Serial(port=SERIAL_PORT)
            self._serial.baudrate = SERIAL_BAUDRATE
            self._serial.timeout = TIMEOUT
            self.empty()
        except:
            #Logger().exception("SerialDriver._init()")
            raise

    def shutdown(self):
        self._serial.close()

    def setRTS(self, level):
        """ Set RTS signal to specified level.

        @param level: level to set to RTS signal
        @type level: int
        """
        self._serial.setRTS(level)

    def setDTR(self, level):
        """ Set DTR signal to specified level.

        @param level: level to set to DTR signal
        @type level: int
        """
        self._serial.setDTR(level)

    def setTimeout(self, timeout):
        self._serial.timeout = timeout

    def empty(self):
        if hasattr(self._serial, "inWaiting"):
            self.read(self._serial.inWaiting())

    def write(self, data):
        #Logger().debug("SerialDriver.write(): data=%s" % repr(data))
        size = self._serial.write(data)
        return size

    def read(self, size):
        data = self._serial.read(size)
        #Logger().debug("SerialDriver.read(): data=%s" % repr(data))
        if size and not data:
            raise IOError("Timeout while reading on serial bus (size=%d, data=%s)" % (size, repr(data)))
        else:
            return data

class MerlinOrionHardware(object):
    """ Merlin/Orion low-level hardware.
    """
    def __init__(self, axis, driver):
        """ Init the MerlinOrionHardware object.
        """
        super(MerlinOrionHardware, self).__init__()

        self.__axis = axis
        self.__driver = driver
        self.__encoderFullCircle = None

    def __decodeAxisValue(self, strValue):
        """ Decode value from axis.

        Values (position, speed...) returned by axis are
        32bits-encoded strings, low byte first.

        @param strValue: value returned by axis
        @type strValue: str

        @return: value
        @rtype: int
        """
        value = 0
        for i in xrange(3):
            value += eval("0x%s" % strValue[i*2:i*2+2]) * 2 ** (i * 8)
        #print "dav: %s" % value
        return value

    def __encodeAxisValue(self, value):
        """ Encode value for axis.

        Values (position, speed...) to send to axis must be
        32bits-encoded strings, low byte first.

        @param value: value
        @type value: int

        @return: value to send to axis
        @rtype: str
        """
        strHexValue = "000000%s" % hex(value)[2:]
        strValue = strHexValue[-2:] + strHexValue[-4:-2] + strHexValue[-6:-4]
        #print "value %s, eav: %s" % (value, strValue.upper())

        return strValue.upper()

    def __encoderToAngle(self, codPos):
        """ Convert encoder value to degres.

        @param codPos: encoder position
        @type codPos: int

        @return: position, in °
        @rtype: float
        """
        value = (codPos - ENCODER_ZERO) * 360. / self.__encoderFullCircle
        #print "e2a: %f" % value
        return value

    def __angleToEncoder(self, pos):
        """ Convert degres to encoder value.

        @param pos: position, in °
        @type pos: float

        @return: encoder position
        @rtype: int
        """
        value = int(pos * self.__encoderFullCircle / 360. + ENCODER_ZERO)
        #print "a2e: %d" % value
        return value


    def __sendCmd(self, cmd, param=""):
        """ Send a command to the axis.

        @param cmd: command to send
        @type cmd: str

        @return: answer
        @rtype: str
        """
        cmd = "%s%d%s" % (cmd, self.__axis, param)
        #print "cmd: %s"% cmd
        #print "MerlinOrionHardware.__sendCmd(): axis %d cmd=%s" % (self.__axis, repr(cmd))
        for nbTry in range(3):
            try:
                answer = ""
                self.__driver.write(":%s\r" % cmd)
                c = ''
                while c not in ('=', '!'):
                    c = self.__driver.read(1)
                    #Lprint "MerlinOrionHardware.__sendCmd(): c=%s" % repr(c)
                if c == '!':
                    c = self.__driver.read(1) # Get error code
                    raise IOError("Error in command %s (err=%s)" % (repr(cmd), c))
                answer = ""
                while True:
                    c = self.__driver.read(1)
                    #print "MerlinOrionHardware.__sendCmd(): c=%s" % repr(c)
                    if c == '\r':
                        break
                    answer += c

            except IOError:
                 print "MerlinOrionHardware.__sendCmd(): axis %d can't sent command %s. Retrying..." % (self.__axis, repr(cmd))
            else:
                break
        else:
            raise HardwareError("axis %d can't send command %s" % (self.__axis, repr(cmd)))
        #print "MerlinOrionHardware.__sendCmd(): axis %d ans=%s" % (self.__axis, repr(answer))

        return answer

    def init(self):
        """ Init the MerlinOrion hardware.

        Done only once per axis.
        """

        # Stop motor
        self.__sendCmd("L")

        # Check motor?
        self.__sendCmd("F")

        # Get firmeware version
        value = self.__sendCmd("e")
        print "MerlinOrionHardware.init(): firmeware version=%s" % value

        # Get encoder full circle
        value = self.__sendCmd("a")
        self.__encoderFullCircle = self.__decodeAxisValue(value)
        print "MerlinOrionHardware.init(): encoder full circle=%s" % hex(self.__encoderFullCircle)

        # Get sidereal rate
        value = self.__sendCmd("D")
        print "MerlinOrionHardware.init(): sidereal rate=%s" % hex(self.__decodeAxisValue(value))

    def read(self):
        """ Read the axis position.

        @return: axis position, in °
        @rtype: float
        """
        value = self.__sendCmd("j")
        self.__driver.releaseBus()
        pos = self.__encoderToAngle(self.__decodeAxisValue(value))
        return pos

    def setOutput(self, state):
        """ Set output state.

        @param state: new state of the the output
        @type state: bool
        """
        self.__sendCmd("O", str(int(state)))

    def drive(self, pos):
        """ Drive the axis.

        @param pos: position to reach, in °
        @type pos: float
        """
        strValue = self.__encodeAxisValue(self.__angleToEncoder(pos))
        self.__sendCmd("L")
        self.__sendCmd("G", "00")
        #self.__sendCmd("j")
        self.__sendCmd("S", strValue)
        self.__sendCmd("J")

    def stop(self):
        """ Stop the axis.
        """
        self.__sendCmd("L")

    def startJog(self, dir_, speed):
        """ Start the axis.

        @param dir_: direction ('+', '-')
        @type dir_: str

        @param speed: speed
        @type speed: int
        """
        self.__sendCmd("L")
        if dir_ == '+':
            self.__sendCmd("G", "30")
        elif dir_ == '-':
            self.__sendCmd("G", "31")
        else:
            raise ValueError("Axis %d dir. must be in ('+', '-')" % self.__axis)
        self.__sendCmd("I", self.__encodeAxisValue(speed))
        self.__sendCmd("J")

    def setSpeed(self, speed):
        self.__sendCmd("I", self.__encodeAxisValue(speed))

    def getStatus(self):
        """ Get axis status.

        @return: axis status
        @rtype: str
        """
        return self.__sendCmd("f")



def test():
    """ Test.
    """
    driver = SerialDriver()
    yaw = MerlinOrionHardware(YAW_AXIS, driver)
    pitch = MerlinOrionHardware(PITCH_AXIS, driver)
    shutter = MerlinOrionHardware(SHUTTER_AXIS, driver)

    lst = []
    lst.append(Point(11.4,14.7))
    lst.append(Point(14.7,1.5))
    lst.append(Point(24.2,14.1))
    lst.append(Point(29.4,2.0))
    bez = BezierCurve(lst)  # initialise the Bezier curve with control points
    bez.findsteps()         # find the minimum number of steps to traverse the curve
    time.sleep(3)           # give time to decide on the step or press Ctrl+C to quit
    cp = bez.bezier()       # calculate the curve points (equals to # of steps + 1)

    yaw.init()
    pitch.init()

    for i in range(len(cp)):
        cp[i].pprint()
        yaw.drive(cp[i].x)
        pitch.drive(cp[i].y)
        while yaw.getStatus()[1] != '0':
            time.sleep(0.1)
        while pitch.getStatus()[1] != '0':
            time.sleep(0.1)
        # Stabilization delay
        time.sleep(0.5)
        # press the shutter 1/2 second time value
        shutter.setOutput(1)
        time.sleep(0.5)
        shutter.setOutput(0)
        # delay between shots
        time.sleep(2.0)

    driver.shutdown()


if __name__ == "__main__":
    """ test() won't be executed if the module is imported.
    """
    test()

For those who are not familiar with the Bezier curve, please see her http://en.wikipedia.org/wiki/B%C3%A9zier_curve. You have to update the code part below to match your positions.
Code:
    lst.append(Point(11.4,14.7))
    lst.append(Point(14.7,1.5))
    lst.append(Point(24.2,14.1))
    lst.append(Point(29.4,2.0))

The example video is http://www.vimeo.com/10767739. This is just out of the camera. The head is not that accurate in moving the curved path so it is resulting in zig-zag motion.

In order to solve this problem, the DeSahker plugin http://www.sundancemediagroup.com/articles/deshaker_guide.htm for VirtualDub is used to stabilize the shot. This is mandatory step for making the video look steady. After the stabilization, you can see the video at http://www.vimeo.com/10767843


Jones


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 08, 2010 6:45 am 
Offline
Member
User avatar

Joined: Wed Dec 07, 2005 6:21 pm
Posts: 5826
Location: Grenoble, France
This is really great work, Jones!

_________________
Frédéric

Canon 20D + 17-40/f4 L USM + 70-200/f4 L USM + 50/f1.4 USM
Merlin/Orion panohead + Papywizard on Nokia N800 and HP TC-1100


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 08, 2010 8:57 am 
Offline
Member
User avatar

Joined: Sun Nov 25, 2007 11:12 pm
Posts: 1341
Location: Mont-Saint-André (Belgium)
Excellent addition Jones,

This get some forms now ...

(P.S. For using this last version Pyserial should be installed also ...)

_________________
:cool: Claude :cool:
Merlin + Papywizard on Windows 7 & Nokia 770 § N810 & Acer (Netbook) + PanoramaApp Androïd + Deltawave PapyMerlin BT + Autopano
Spherical Pano (180 x 360) with Canon 40D + Canon EF-S 10-22mm f/3.5-4.5 Zoom & Pôle Pano with Canon 5D MK2 and shaved Tokina 10-17 3.5-4.5 AF DX Fisheye
Gigapixel photography with Nikon D200 + Sigma 70-200 F 2.8 EX DG APO HSM


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 08, 2010 11:58 am 
Offline
Member
User avatar

Joined: Wed Dec 07, 2005 6:21 pm
Posts: 5826
Location: Grenoble, France
Do you plan to integrate the Bezier generator to the Timelapse preset generator?

_________________
Frédéric

Canon 20D + 17-40/f4 L USM + 70-200/f4 L USM + 50/f1.4 USM
Merlin/Orion panohead + Papywizard on Nokia N800 and HP TC-1100


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 09, 2010 12:21 am 
Offline
Member

Joined: Tue Mar 10, 2009 1:43 am
Posts: 146
Location: Colorado, USA
Thank you Frederic and Claude!

Yes. I have a Bezier generator for time lapse as well. I have not yet merged that to the current time lapse generator. I am thinking of leaving the old one as it is and upload the new one. The reason is that the Bezier curve becomes a straight line (which the old time lapse is handling) if only two control points are used. That straight line could be any orientation and finer movements with the new Bezier one. The new generator also includes the general rule of thumb of moving the head for at least 7 seconds to cross the FOV of the lens that will result in a smoother motion.

Is there a rounding done inside the Papywizard code when reading the yaw and pitch values from the preset xml? The new generator generates up to 4 decimal place of accuracy for the yaw and pitch values. I want to make sure it is sent all the way to head without any rounding happening in the middle.

Jones


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 09, 2010 12:23 am 
Offline
Member

Joined: Tue Mar 10, 2009 1:43 am
Posts: 146
Location: Colorado, USA
The only thing I have not figured out using the Bezier is that how to do the smooth in/out or ramp up/down. I'll upload the new one soon here.

Jones


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 09, 2010 6:22 am 
Offline
Member
User avatar

Joined: Wed Dec 07, 2005 6:21 pm
Posts: 5826
Location: Grenoble, France
I don't think I round values from preset, but the Merlin accuracy does ;)

_________________
Frédéric

Canon 20D + 17-40/f4 L USM + 70-200/f4 L USM + 50/f1.4 USM
Merlin/Orion panohead + Papywizard on Nokia N800 and HP TC-1100


Top
 Profile  
 
 Post subject:
PostPosted: Sat Apr 10, 2010 6:59 am 
Offline
Member

Joined: Tue Mar 10, 2009 1:43 am
Posts: 146
Location: Colorado, USA
Thanks for the info Frederic. I have uploaded the new preset generator in a separate thread.

Jones


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 39 posts ]  Go to page Previous  1, 2

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group