Integrated movement service creation

Hello all.  If you havent kept up with my progress in the shoutbox, I am in the process of finishing  a highly advanced head mod  ( the "advanced inmoov head").  I have to control 28 servos using, for now,   2  16-channel servo drivers.

However, i need a service that will allow for the many servos ( there are at least 8 in the lips) in the head to be controlled simultaneously.  I also need to be able to save the positions of servos in a way similar to the InmoovGestureCreator and have the ability to  chain these "gestures" together to make complexed emotional movements on inmoov's face.  

Along with this advanced control over multiple servos, I need a to be able to dynamically change the limits of a servo depending on the position of another servo. For example, my  advanced inmoov eyes have mechanical limits due to the eyelids. When the eye is horizontally in one of the corners of the eye socket, it is unable to move much, if at all in the vertical direction without hitting the eyelids (just like a human eyeball). Thus, it is necessary to be able to change the limits of a servo (say the up and down servo for the eyes), depending on the position of another servo.    

@Calamity,  I heard from Kwatters that you've made great progress on an integrated movement service and he suggested I make this post so that all of us could discuss how to make this service a reality. A lot of us here on MRL, including myself, would appreciate any assistance you could offer.  

In any case, lets get to work !  :)

 

Alexinator40's picture

A github ticket was made

A github ticket was made about a week ago for brainstorming this concept. Comments can either be post here or there.    

https://github.com/MyRobotLab/myrobotlab/issues/228

calamity's picture

Hi Alexinator I would gladly

Hi Alexinator

I would gladly assist anyone who want assistance, be with the integrated movement service or any thing that I have some knowledge.

To do what you want with your advanced head, you could use a structure similar to what I have done with integrated movement service (or I think it sould be fairly easy to add it the the integrated movement service.

  • Have a service that define and keep track of the position of your part
  • the services runs threads that constantly computes the position of the part and adjust their position.

So to be more specific with your case, you could have a service that define and keep track of the position and current limits of your 'eyes' servo and a thread that compute the position and limits of the servo based on what your service know. ie if servo A is in position x, set the limits of servo B to x/y then move it to be inside those limits. 

I hope I'm somewhat clear the the strategy I describe. Personnaly, I like this because it could easily create behaviour instead of scripted gestures

Alexinator40's picture

Hey Calamity. I didnt

Hey Calamity. I didnt remember to ask this to you last night. Do you have source code for your integrated movement service you could provide us to look over? We want to see if we could use your code as a basis for a dynamic limits service, a servo orchestrator service, or both. We also would possibly like to generalize your code from just inmoov to any robot that uses mrl so many other users can improve their robots' movements.

calamity's picture

the

the service https://github.com/MyRobotLab/myrobotlab/blob/develop/src/main/java/org/myrobotlab/service/IntegratedMovement.java

all the other class relate to it

https://github.com/MyRobotLab/myrobotlab/tree/develop/src/main/java/org/...

hairygael's picture

Hello, Because you mention in

Hello,

Because you mention in the shoutbox not having played with codes yet, here is a script you could use to run some tests on your 27 servo head. This is not relevant with integrated movement service, but there is servo syncing for the eyelids.

It also can be usefull before the refactor of the servoOrchestrator is fully working.

 

#########################################
# Servo test by Gael For Alexinator40.py
# more info @: http://myrobotlab.org/service/Servo
#########################################
# uncomment for virtual hardware
# virtual = True

eyeXPin = 22
eyeYPin = 24
eyeLidRightPin = 12
eyeLidLeftPin = 13

# port = "/dev/ttyUSB0"
port = "COM15"

# start optional virtual arduino service, used for test
if ('virtual' in globals() and virtual):
    virtualArduino = Runtime.start("virtualArduino", "VirtualArduino")
    virtualArduino.connect(port)

# create a servo controller and a servo
arduino = Runtime.start("arduino","Arduino")
eyeX = Runtime.start("eyeX","Servo")
eyeY = Runtime.start("eyeY","Servo")
eyeLidRight = Runtime.start("eyeLidRight","Servo")
eyeLidLeft = Runtime.start("eyeLidLeft","Servo")

# initialize arduino
# linux or macos -> arduino.connect("/dev/ttyUSB0")
print("connecting arduino to serial port")
arduino.connect(port)

# servo01.setMinMax(0, 180)
eyeX.map(0, 180, 0, 180)
eyeY.map(0, 180, 0, 180)
eyeLidRight.map(0, 180, 0, 180)
eyeLidLeft.map(0, 180, 0, 180)

# set rest position
eyeX.setRest(90)
eyeY.setRest(90)
eyeLidRight.setRest(90)
eyeLidLeft.setRest(90)

# attach servo
print("attaching servo with pins to controller")
eyeX.attach(arduino.getName(), eyeXPin)
eyeY.attach(arduino.getName(), eyeYPin)
eyeLidRight.attach(arduino.getName(), eyeLidRightPin)
eyeLidLeft.attach(arduino.getName(), eyeLidLeftPin)

# auto disable - this enables (starts pwm) before a movement
# and disables (stops pwm) after a movement
eyeX.setAutoDisable(True)
eyeX.disableDelayIfVelocity=1000 # grace period for autoDisable
eyeY.setAutoDisable(True)
eyeY.disableDelayIfVelocity=1000
eyeLidRight.setAutoDisable(True)
eyeLidRight.disableDelayIfVelocity=1000
eyeLidLeft.setAutoDisable(True)
eyeLidLeft.disableDelayIfVelocity=1000

# speed changes
print("speed changes")
eyeX.setVelocity(80) ## Low velocity
eyeY.setVelocity(80)
eyeLidRight.setVelocity(80)
eyeLidLeft.setVelocity(80)

eyeX.moveTo(0) # we cannot use moveToBlocking if servo velocity is set to -1 ( max ) !!
eyeY.moveTo(180)
sleep(2)

# sync eyeLidRight with eyeLidLeft
# now eyeLidRight will be a slave to eyeLidLeft
print("syncing eyeLidRight with eyeLidLeft")
eyeLidRight.sync(eyeLidLeft)

eyeLidLeft.moveTo(10)
sleep(0.5)

eyeLidLeft.moveTo(179)
sleep(0.5)

eyeLidLeft.moveTo(10)
sleep(0.5)

# moving to rest position
print("eyeLidLeft rest")
eyeLidLeft.rest()
sleep(2)

Alexinator40's picture

Hey Gael! Thanks for

Hey Gael! Thanks for commenting and providing this helpful script ! :)  Although the advanced head actually has 4 eyelids that can be controlled, I am sure I can figure out how to edit the code you provided to test the eyelid and eye functionality! I hope you will like my mod when its released! By the way, my advanced head will support your new neck, though i might have to make a minor modification or two to the neck base (where the pistons attach to) so that I can fit all the servos in :)  

Spawn32's picture

Adafruit16

Tried to get this script working with the Adafruit16 servo controller, but with my python skill's (read none)

it throws me errors, and no line numbers :/

Could someone take a look at it:

error:

-----TypeError: compile() expected string without null bytes at org.python.core.Py.TypeError(Py.java:265) at org.python.core.Py.compile_flags(Py.java:2198) at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:267) at org.myrobotlab.service.Python$PIThread.run(Python.java:159)------
 

 

Script:

#########################################
# Servo test by Gael For Alexinator40.py
# more info @: http://myrobotlab.org/service/Servo
#########################################
# uncomment for virtual hardware
# virtual = True
 
eyeLidRightPin = 0
eyeLidLeftPin = 1
eyeXPin = 2
eyeYPin = 3
 
# port = "/dev/ttyUSB0"
port = "COM4"
 
# start optional virtual arduino service, used for test
if ('virtual' in globals() and virtual):
    virtualArduino = Runtime.start("virtualArduino", "VirtualArduino")
    virtualArduino.connect(port)
 
# create a servo controller and a servo
arduino = Runtime.start("arduino","Arduino")
# initialize arduino
# linux or macos -> arduino.connect("/dev/ttyUSB0")
print("connecting arduino to serial port")
arduino.connect(port)
 
# Start the Adafruit16CServodriver that can be used for all PCA9685 devices
print("connecting adaFruit16c to arduino i2c")
adaFruit16c = Runtime.start("AdaFruit16C","Adafruit16CServoDriver")
adaFruit16c.attach("arduino","0","0x40")
 
eyeX = Runtime.start("eyeX","Servo")
eyeY = Runtime.start("eyeY","Servo")
eyeLidRight = Runtime.start("eyeLidRight","Servo")
eyeLidLeft = Runtime.start("eyeLidLeft","Servo")
 
# servo01.setMinMax(0, 180)
eyeX.map(0, 180, 0, 180)
eyeY.map(0, 180, 0, 180)
eyeLidRight.map(0, 180, 0, 180)
eyeLidLeft.map(0, 180, 0, 180)
 
# set rest position
eyeX.setRest(90)
eyeY.setRest(90)
eyeLidRight.setRest(90)
eyeLidLeft.setRest(90)
 
# attach servo
print("attaching servos with pins to controller")
#eyeX.attach(arduino.getName(), eyeXPin)
#eyeY.attach(arduino.getName(), eyeYPin)
#eyeLidRight.attach(arduino.getName(), eyeLidRightPin)
#eyeLidLeft.attach(arduino.getName(), eyeLidLeftPin)
 
eyeX.attach(adaFruit16c,eyeXPin)
eyeY.attach(adaFruit16c,eyeYPin)
eyeLidRight.attach(adaFruit16c,eyeLidRightPin)
eyeLidLeft.attach(adaFruit16c,eyeLidLeftPin)
 
#thumb.attach(adaFruit16c,3)
#elbow.attach(adaFruit16c,8)
 
# auto disable - this enables (starts pwm) before a movement
# and disables (stops pwm) after a movement
eyeX.setAutoDisable(True)
eyeX.disableDelayIfVelocity=1000 # grace period for autoDisable
eyeY.setAutoDisable(True)
eyeY.disableDelayIfVelocity=1000
eyeLidRight.setAutoDisable(True)
eyeLidRight.disableDelayIfVelocity=1000
eyeLidLeft.setAutoDisable(True)
eyeLidLeft.disableDelayIfVelocity=1000
 
# speed changes
print("speed changes")
eyeX.setVelocity(80) ## Low velocity
eyeY.setVelocity(80)
eyeLidRight.setVelocity(80)
eyeLidLeft.setVelocity(80)
 
eyeX.moveTo(0) # we cannot use moveToBlocking if servo velocity is set to -1 ( max ) !!
eyeY.moveTo(180)
sleep(2)
 
# sync eyeLidRight with eyeLidLeft
# now eyeLidRight will be a slave to eyeLidLeft
print("syncing eyeLidRight with eyeLidLeft")
eyeLidRight.sync(eyeLidLeft)
 
eyeLidLeft.moveTo(10)
sleep(0.5)
 
eyeLidLeft.moveTo(179)
sleep(0.5)
 
eyeLidLeft.moveTo(10)
sleep(0.5)
 
# moving to rest position
print("eyeLidLeft rest")
eyeLidLeft.rest()
sleep(2)
hairygael's picture

Hello Spawn32, You need to

Hello Spawn32,

 You need to specify in your script that you want to use the Adafruit pins.

@Alexinator40, what is the skin going to be made with for your 27 servo head?

As a professionnal, I use silicone casting to create skin effects. Nowadays, silicone and latex foam are pretty much the only materials  that can be used to create skin to be soft enough to give realistic human facial movements.

I have tested Ninjaflex or Filaflex a while back, but there is no way it can be used for face movements. (Maybe in the future)

I have seen a prototype 3D printer that could print  silicone Soft Touch texture surface. The Soft Touch Effect can be found on many devices nowadays, phone, remote controller, car key holder...

I am truly wondering what is your plan.

:)

#########################################
# Servo test by Gael For Spawn32Adafruit16C.py
# more info @: http://myrobotlab.org/service/Servo

# more info @: http://myrobotlab.org/service/Adafruit16CServoDriver
#########################################
# uncomment for virtual hardware
# virtual = True

# eyeXPin = 22
# eyeYPin = 24
# eyeLidRightPin = 12
# eyeLidLeftPin = 13

# port = "/dev/ttyUSB0"
port = "COM15"

# start optional virtual arduino service, used for test
if ('virtual' in globals() and virtual):
    virtualArduino = Runtime.start("virtualArduino", "VirtualArduino")
    virtualArduino.connect(port)
adaFruit16c = Runtime.start("AdaFruit16C","Adafruit16CServoDriver")

# create a servo controller and a servo
arduino = Runtime.start("arduino","Arduino")
eyeX = Runtime.start("eyeX","Servo")
eyeY = Runtime.start("eyeY","Servo")
eyeLidRight = Runtime.start("eyeLidRight","Servo")
eyeLidLeft = Runtime.start("eyeLidLeft","Servo")

# initialize arduino
# linux or macos -> arduino.connect("/dev/ttyUSB0")
print("connecting arduino to serial port")
arduino.connect(port)
adaFruit16c.attach("arduino","0","0x40")

# servo01.setMinMax(0, 180)
eyeX.map(0, 180, 0, 180)
eyeY.map(0, 180, 0, 180)
eyeLidRight.map(0, 180, 0, 180)
eyeLidLeft.map(0, 180, 0, 180)

# set rest position
eyeX.setRest(90)
eyeY.setRest(90)
eyeLidRight.setRest(90)
eyeLidLeft.setRest(90)

# attach servo to the Adafruit16CServoDriver
print("attaching servo to the Adafruit16CServoDriver")
eyeX.attach(adaFruit16c,22)
eyeY.attach(adaFruit16c,24)
eyeLidRight.attach(adaFruit16c,12)
eyeLidLeft.attach(adaFruit16c,13)

# auto disable - this enables (starts pwm) before a movement
# and disables (stops pwm) after a movement
eyeX.setAutoDisable(True)
eyeX.disableDelayIfVelocity=1000 # grace period for autoDisable
eyeY.setAutoDisable(True)
eyeY.disableDelayIfVelocity=1000
eyeLidRight.setAutoDisable(True)
eyeLidRight.disableDelayIfVelocity=1000
eyeLidLeft.setAutoDisable(True)
eyeLidLeft.disableDelayIfVelocity=1000

# speed changes
print("speed changes")
eyeX.setVelocity(80) ## Low velocity
eyeY.setVelocity(80)
eyeLidRight.setVelocity(80)
eyeLidLeft.setVelocity(80)

eyeX.moveTo(0) # we cannot use moveToBlocking if servo velocity is set to -1 ( max ) !!
eyeY.moveTo(180)
sleep(2)

# sync eyeLidRight with eyeLidLeft
# now eyeLidRight will be a slave to eyeLidLeft
print("syncing eyeLidRight with eyeLidLeft")
eyeLidRight.sync(eyeLidLeft)

eyeLidLeft.moveTo(10)
sleep(0.5)

eyeLidLeft.moveTo(179)
sleep(0.5)

eyeLidLeft.moveTo(10)
sleep(0.5)

# moving to rest position
print("eyeLidLeft rest")
eyeLidLeft.rest()
sleep(2)

Spawn32's picture

Still same error :/

Hi Gael :)

Thanks for your help, but i still get the same exact error :/

Not sure what is wrong whit the script, btw we / i  use Nixie...

 

just for test i used this, and that works:

 

#########################################
# Adafruit16CServoDriver.py
# description: servo control
# categories: servo control
# more info @: http://myrobotlab.org/service/Adafruit16CServoDriver
#########################################
 
# This example shows how to use the Adafruit16CServoDriver
# It can be used with Arduino, RasPi or Esp8266_01
# From version 1.0.2316 use attach instead of setController
#
# config
port = "COM4"
# Code to be able to use this script with virtalArduino
if ('virtual' in globals() and virtual):
    virtualArduino = Runtime.start("virtualArduino", "VirtualArduino")
    virtualArduino.connect(port)
# Start the Adafruit16CServodriver that can be used for all PCA9685 devices
adaFruit16c = Runtime.start("AdaFruit16C","Adafruit16CServoDriver")
#
# This part of the script is for the Arduino
# Comment it out the three lines below if you don't use the Arduino
# Change COM3 to the port where your Arduino is connected
arduino = Runtime.start("arduino","Arduino")
arduino.connect(port)
adaFruit16c.attach("arduino","0","0x40")
#
# This part of the script is if you use the GPOI pins of the Raspberry PI
# Uncomment the two lines below if you use the RasPi
# raspi = Runtime.createAndStart("raspi","RasPi")
# adaFruit16c.attach("raspi","1","0x40")
#
# This part of the script is if you use the Esp8266_01 service
# Uncomment it the two lines below if you duse the Esp8266_01
# esp = Runtime.start("esp","Esp8266_01")
# adaFruit16c.attach("esp","1","0x40")
#
# This part is common for both devices and creates two servo instances
# on port 3 and 8 on the Adafruit16CServoDriver
# Change the names of the servos and the pin numbers to your usage
lefteyelidupper = Runtime.start("LeftEyeLidUpper", "Servo")
lefteyelidlower = Runtime.start("LeftEyeLidLower", "Servo")
righteyelidupper = Runtime.start("RightEyeLidUpper", "Servo")
righteyelidlower = Runtime.start("RightEyeLidLower", "Servo")
# attach it to the pwm board 
lefteyelidupper.attach(adaFruit16c,0)
lefteyelidlower.attach(adaFruit16c,2)
righteyelidupper.attach(adaFruit16c,1)
righteyelidlower.attach(adaFruit16c,3)
# When this script has been executed you should be able to
# move the servos using the GUI or using python
Spawn32's picture

Update:

Hi Gael,

The scrip works like it should if i start it from the command line, but gives the error above if i run it in the "python" window in the GUI, no ider why, but at least it works, thanks, now we have somwthing to test the servos with :)

 

/Spawn

hairygael's picture

I suspect a Nixie issue. It

I suspect a Nixie issue.

It works in real and virtualy in the Manticore.

Alexinator40's picture

Mouth Mechanism

Hey Gael. Sorry. Been a bit busy as im on vacation. The mouth mech is reversed engineered from this video to fit in inmoov normal head size ( https://m.youtube.com/watch?v=Ke2lJfY4haM&t=1s). The lips will be exactly like those lips in the video which bend just fine ( ive checked by manually bending the pieces), but ill have a realistic lip cover to add on top of those lip pieces. The only other flexible pieces for the mouth and jaw  are going to be some covers/cover from the jaw/lips down to the neck. I still think these covers should stretch enough given the right print settings, but if not, ill do silicone.   The rest of the parts ( for the most part) will be done in non-flexible material.
 
 
 
 
hairygael's picture

Wow, this is impressive

Wow, this is impressive video.

Adjusting all those servos and pices within InMoov head is going to be a real challenge.

Eager to see the results!