Project

General

Profile

Useful links

by Aditya Bhutada : Universal Event and Motion Editor for Robots Theatre

Realization of Tai-chi Motion Using a Humanoid Robot

Towards a Unified Framework for Intelligent Robotics

Tailored Real-Time Simulation for Teams of Humanoid Robots

iXs Research Corp. KONDO KHR - all documents translated to English

HeartToHeart Ver1.1 for English (executable)
HeartToHeart3 Software for KHR-1 (executable)
HeartToHeart3 Instruction manual

Acceleration sensor for KHR-1

KHR-1 Assembly Manual

"

Simulating KONDO KHR-1

Download and Install
  1. Microsoft Visual C#
  2. Microsoft Robotics Developer Studio
MRDS Samples
Unzip the samples package into your RDS installation folder
Follow the instructions in the Read Me (which you can find in the CodePlexSamples\Documentation folder)
Run Setup.cmd

Using the KHR-1 Service Sample


Image from

Setting up for the KHR-1

RCB-1 - PC Setup
Testing Servo Connections
Startup

Video: Kondo KHR-1HV, Walk sequences with no gyro


Labeling of the Servo motors


RCB-1 Controller Board

Refer to Refernces

Using An Emotional Mimicking Humanoid Biped Robot and Its Quantum Control Based on the Constraint Satisfaction Model

(extracting relevant information)


2. KHR-1 Hardware, Assembly and Maintenance.

" ...  In order to ensure that the robot was ready from the hardware perspective, several connections should be checked: 
(1) The best way to adjust the servo hones is illustrated in Figure 1. The servo hone should be aligned with the middle hole of the cross arm part. 
(2) The KHR-1 has two servo controllers located on the back of the robot. Each RCB-1 is capable to control up to 12 servos, and they can store data motions designed by the user. Figure 2 shows the two RCB-1 and their connections. Additionally, the Gyroscope is connected between channels 17 and 23, and the Bluetooth is adapted to the serial connection. 
(3) It is important that the user adjusts screws from time to time during assembly/test. Additionally, the trim function [29] was unable to correct some of the servos.  ..." 

3.   Motion-related KHR-1 Software

" Heart to Heart is the original company software to program and control the KHR-1.  The PC interacts with the KHR-1 through the RCB-1 boards which are connected via RS-232 cable. Each board controls the upper and lower body of the robot respectively.  The KHR-1 has 17 servo motors. In order to facilitate the programming and controlling of each servo through Heart to Heart software, they have been labeled with numbers as is shown in Figure 3.  Each channel shown in the main window of the software represents a specific servo. 

3.1. SYNC Function   The SYNC function (see Figure 4) allows real time communication between the KHR-1 and the Heart to Heart software. When the robot is connected to the PC it is necessary to set the SYNC function in its ON position because it allows to control the robot. If the user wants to make any changes on the servos, create new positions and motion files, the SYNC function must be  ON.

3.2. How to get started.  To install the project one needs: HBP files, Visual Basic 6.0 (this is important because you need a “com object.”), OpenCV (version 3.1 b). You will also need a version of Visual Basic that supports the com object.  We found that VB6 worked well. 
...
 If you are starting from scratch, you will need to generate a method for communicating with the KHR-1 through a com port.  That is why it’s important to use VB 6.0, later versions do not have this option yet. 
... " 

5.  Gyroscope.

Bipedal humanoid robots are inherently unstable.  Unlike wheeled robots, humanoids have a high center of gravity and must balance carefully in order not to tip over as they move.  While it is possible to achieve balance in the absence of feedback sensors, slight variations in the environment often cause imbalance and result in a fall.  Several approaches have been taken to improve the stability of two legged robots.  Installation of large foot pads aid in stability, but can be cumbersome in quick maneuvers.  

One way to improve stability without adding area to the feet of the robot is to add a feedback mechanism. Feedback is present in many natural and man-made systems.  The principle of negative feedback and control theory has been instrumental in achieving reliability in mechanical and electrical systems. In order to improve the stability of the bipedal robot, a compensating gyroscope was installed.  This unit was manufactured by the Kondo company, and was designed specifically for the KHR-1.  Thus, it was trivial to simply plug the gyroscope into the cabling without modification of wiring. 
The gyroscope works as follows:  Each servo motor receives a pulse width modulated (PCM) square wave signal from the controller board on the robot. The controller board encodes position commands to each servo motor by modifying the duty cycle of the PCM input. The gyroscope is wired in series with the servo motors to be controlled.  That is to say that the PCM signal passes through the gyroscope wherein the duty cycle is modified according to the instantaneous acceleration in the axis to which the gyroscope is sensitized. This has the effect that sudden acceleration will result in compensatory movement of the servos to correct and maintain balance.

The gyroscope installed on this robot is sensitive to acceleration in only one of two possible corrective axes.  One pair of servos controls side to side balance at the base of the feet.  Another can provide front to back correction by changing the angle of bend at the knee joints in the legs. It  would be necessary to have two separate gyroscopes to provide balance feedback for both front to back and side to side motions. 

We have only one gyroscope, and chose to control side to side balance.  Our choice for side to side motion was due to the fact that additional hardware is necessary to program the servos 22 and 16.  According to the translated instructions, the “Servo Manager” application along with the special cable available from robosavvy.com is necessary to program servos 22 and 16 to be able to accept the signal from the gyroscope.  This is in contrast with the software-free modification of the side to side axis. In any case, installing the gyro helped with movement stability and we plan to add also the second gyro.


#-----------------------------------------------#
#  ECE 478  -  Genetic Alorithm for KHR-1       #
#  Purpose: Motion Creator and File Generation  #
#  Programmers: Nimrod McDade and Charlie Wu    #
#  Written in Python Programming Language       #
#  October-November 2009                        #
#  File Name: genetic5.py                       #
#-----------------------------------------------# correct comments in red

import random
import sys
#-------------------------------------------------------------------------------------------------------------------- 
# This is a GAMotion class
class GAMotion():

   #constructor, initialize each member variable
   def __init__(self,ModelPosition,Channels,Population,MutateRate,MaximumFitness):

        self.ModelPosition = ModelPosition   #A model motion data(genes) used for calculate fitness
        self.Channels = Channels             #number of servo channels (17)
        self.Population = Population         #initial starting population
        self.MUTATION_RATE = MutateRate      #mutation rate (Expressed as %)
        self.MaximumFitness = MaximumFitness #Maximum fitness value (0 to 15) max: 15
        self.TotalOfFitness = 0              #total fitness of each organism (line of motion data)
        self.PositionPopulations=[]          #populations of servo motion data(genes) for current generation
        self.nextPositionPopulations=[]      #populations of servo motion data(genes) for next generation
        self.currentPopulationFitness=[]     #store fitness values for each array of motion data of the current pop.
        self.finalGaPosition=[]              #final motion servo data generated by GA
        self.templist=[]

        if n == 1:
           print "Channels:", Channels
           print "Population:", Population
           print "Max Fit Value:", MaximumFitness
           print "Mutation Rate:", MutateRate, "%" 
           print "Running Genetic Algorithm..." 

        print "Posture Data for fitness calculation ", n,  ":" 
        print ModelPosition

#--------------------------------------------------------------------------------------------------------------------  
   # Method to allocate a population of motions. This is 2-D array or list of list of motion data. Rephrase and explain
   def AllocatePositionPopulation(self):

         # loop from 0 to population size
         for i in range(0,self.Population):
           #call random function to generate a list of servo motion data. Each list should have
           #Random values(-180 to 225) for servo channels. The random generated list is added to the list
           self.PositionPopulations.append(self.GetRandomPositions())
           #current fitness value for each organism in the population is zero to start with
           self.currentPopulationFitness.append(0)
           #call radom function of generate a list of random servo motion data for next generation GOOD COMMENTS HERE
           self.nextPositionPopulations.append(self.GetRandomPositions())

#--------------------------------------------------------------------------------------------------------------------
   # Method to determine which index in the population array to use as parent,
   # using the current fitness counter as a criteria   NOT CLEAR? EXPLAIN BETTER
   def SelectOneParent(self):

      runningTotal=0

      # random select point is between 0 and 25 
      randomSelectPoint=random.randrange(0,self.TotalOfFitness+1)

      # loop through the population and total up fitness values of each list of motion data
      for i in range(0,self.Population):

         runningTotal  = runningTotal + self.currentPopulationFitness[i]
         # return the index as parent if total fitness is greater than random select point
         if  runningTotal >= randomSelectPoint:
             return i

#-------------------------------------------------------------------------------------------------------------------- 
   # this method returns a random servo degree value between -180 and 225  
   # it returns some zeros, some 225, and some between -180 and 225

   def GetRandomPositions(self):

     templist = [] 

     for i in range(1,18):
        if maxRange[i-1] >= 180:
           maxRange[i-1] = 180 - randFreedom
        if minRange[i-1] <= -180:
           minRange[i-1] = -180 + randFreedom
        pos=random.randrange(minRange[i-1],maxRange[i-1],1)
        templist.append(pos)
     return templist

#-------------------------------------------------------------------------------------------------------------------- 

#--------------------------------------------------------------------------------------------------------------------     
  # this method loops through the population to determine the fitness of each organism in the population.
  # For each organism (array of a motion data), compare each of the 24 channel values to the 24 channel values in the
  # model motion data, and record how many values matches or closely matches for the current array motion data. 
  #  EXPLAIN BETTER in more detail

   def EvaluatePositionPopulation(self):

     #fitness value of current motion data is 0

     currentPositionFitnessTally =0
     self.TotalOfFitness=0

     # loop through the population   
     for pop in range(0,self.Population):
          # starting fitness of current motion data is zero 
          currentPositionFitnessTally = 0
          # loop through the 17 channels in each line
          for data in range(0,self.Channels):

             # if a channel value of current motion data match the channel value of the model
             # motion data, increment the fitness counter      
             if self.PositionPopulations[pop][data] == self.ModelPosition[data]:
                  #increment the current fitness counter
                  currentPositionFitnessTally = currentPositionFitnessTally + 1
             # not all channel values can be exactly matched, so give it a freedom of + and - 10 degrees,
             # solution converges faster this way 
             elif self.PositionPopulations[pop][data] < self.ModelPosition[data] + 10 and \
                  self.PositionPopulations[pop][data] > self.ModelPosition[data] - 10 and \
                  self.ModelPosition[data]!=0:
                  currentPositionFitnessTally = currentPositionFitnessTally + 1

          #store the fitness value for each line of motion data COMMENTS INSIDE THE BODY ARE GOOD
          self.currentPopulationFitness[pop]=currentPositionFitnessTally
          # sum up all the fitness values
          self.TotalOfFitness=self.TotalOfFitness+currentPositionFitnessTally

         #if fitness value of current motion data in the population equals to the preset maximum fitness values,
         #then we found a solution, copy the final motion data and return
     if currentPositionFitnessTally == self.MaximumFitness:
            #print "Found max fitness" 
            self.finalGaPosition= self.PositionPopulations[pop]
            #print  self.finalGaPosition
            return True
     else:
            return False

#-------------------------------------------------------------------------------------------------------------------- 
   # Method to generate a populaton of motion data for the next generation using crossover and mutation
   # It chooses two organisms that have the best fitness values for parent one and parent two and uses
   # the parents for cross over to the next generations.

   def ProduceNextGeneration(self):

     # loop through a population of lists of motion data  
     for pop in range (0,self.Population):

        parentone=self.SelectOneParent()  # select best fit motion data to use for parent one 
        parenttwo=self.SelectOneParent()  # select best fit motion data to use for parent two

        # crossover points is between 0 an 17. This case, it is determined by a random function                       
        crossoverpoint = random.randrange(0,self.Channels)

        #loop through the servo channels     
        for ch in range(0,self.Channels):
            #generate a random value between 0 and 1/MUTATION_RATE
            mutateThisGene= random.randrange(0,1/self.MUTATION_RATE)
            # if it is zero, then change the channel value to one between -180 to 255

            if mutateThisGene == 0:
                self.nextPositionPopulations[pop][ch]=random.randrange(-180,225)

            # if it is not zero, then perform crossover      
            else:
                # if a channel value is less then crossover point, then pick parent one and copy to next
                # generation  
                if ch < crossoverpoint:
                      self.nextPositionPopulations[pop][ch]= self.PositionPopulations[parentone][ch]   
                else: #pick parent two and copy to next generation
                      self.nextPositionPopulations[pop][ch]= self.PositionPopulations[parenttwo][ch]   

     #copy the lists of motion data of the next generation back to the current generation array
     for pop in range(0,self.Population):
             for ch in range(0,self.Channels):
                   self.PositionPopulations[pop][ch]=self.nextPositionPopulations[pop][ch]

#--------------------------------------------------------------------------------------------------------------------              

#--------------------------------------------------------------------------------------------------------------------
   # this method allocates the population of lists of motion data and evaluates their fitness  
   def doRunOnce(self):

     generations = 1
     perfectGeneration = False # no solution found

     # allocate random lists of motion data
     self.AllocatePositionPopulation()

     # loop for max population. It returns the generation number if it finds a solution
     while generations < maxGenerations:

       # evaluate the fitness of each line of motion data 
        perfectGeneration = self.EvaluatePositionPopulation()
        # a solution is found then returns 
        if perfectGeneration== True:
            return generations

        # else, produce the next generation and continue 
        self.ProduceNextGeneration()
        generations= generations + 1

     return generations

#--------------------------------------------------------------------------------------------------------------------  
   # return the final list of motion data generated by the Genetic Algorithm
   def getGAPosition(self):

     return self.finalGaPosition
#--------------------------------------------------------------------------------------------------------------------   
# This main function initiates the GAMotion class and passes in the model data,
# number of channels, population size, mutation rate and maximum fitness value.
# (Plenty of references to Monty Python and the Holy Grail throughout)

if __name__=="__main__":

   # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  USER DEFINED PARAMETERS  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
     FitMax = 12                     #maximum fitness value (5 to 15)
     randFreedom = 80               #amount of freedom in servo values
     Mutation = 0.001               #percentage for the mutation rate
     PopulationSize = 200           #Size of population to use for genetic algorithm
     maxGenerations = 800           #Maximum number of generations for finding FitMax
     NumberofMotions = 6           #Number of posture lines to insert in new motion file
     PostureLibraryCount = 10       #Number of postures in posture library... posture.lib
     OutputFile = 'newMotion.csv'   #File name for motion generated by genetic algorithm
     PostureLib = 'posture.lib'     #File name for library of known good KHR-1 postures
     FilePath = '/Users/nimrodm/Desktop/ECE 478/Genetic Algorithms/Motions/'
   # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#

     # Set speed value for new motion
     speed = random.randint(2,6)

     # Open file for output of new motion
     Tim = open(FilePath + OutputFile, 'w')

     for n in range(1,(NumberofMotions + 1),1):
        if n == 1:
           # Open postures file and extract random posture for use as fitness function
           Galahad = open(FilePath + PostureLib, 'r')
           header = Galahad.readline() # Discard header row
           randomInt = random.randint(1, PostureLibraryCount) # Randomly select posture from library
           while randomInt > 0:
              a = Galahad.readline()   # Read line into variable
              randomInt = randomInt - 1
           a = a[0:len(a)-2]  # Remove \n from end of string
           a = a.split(",")   # Split string into list items
           #remove unused channels () from list of servo positions
           Ni = 0
           ModelPosition = []
           minRange = []
           maxRange = []
           for x in range(0,20,1):
              if Ni == 3:
                 Ni = Ni + 2
              elif Ni == 9:
                 Ni = Ni + 3
              elif Ni == 17:
                 Ni = Ni + 1
              elif Ni >= 23:
                 print "It's just a flesh wound" 
                 sys.exit(0)
              else:
                 ModelPosition.append(a[Ni])
                 minRange.append(int(a[Ni])-randFreedom)
                 maxRange.append(int(a[Ni])+randFreedom)
                 Ni = Ni + 1
           Ni = 0                         # Set counter for list items
           for x in ModelPosition[:]:     # For each list item...
              ModelPosition[Ni] = int(x)  # Convert list item to an integer
              Ni = Ni + 1                 # Increment list item counter 
           Galahad.close()                # Close postures file

           #parameters are model data, channels(17), population, mutation rate and maximum fitness value   
           GA = GAMotion(ModelPosition,17,PopulationSize,Mutation,FitMax)
           finalGeneration = GA.doRunOnce()
           if finalGeneration == maxGenerations:
              print "Total Generations:", finalGeneration
              print "Failed to find max fitness, try adjusting MaxFitness or other parameters" 
              sys.exit(0)
           else:
              print "Generations:"+ str(finalGeneration)
              print "GA Posture for motion file:" 
              coconuts = GA.getGAPosition()
              print coconuts   
           if n == 1:
              header = 'NO,DATA_NAME,SPEED,' + header
              Tim.write(header)
           Tim.write(str(n) + ',newMotion,' + str(speed))
           Ni = 0
           for x in range(0,24,1):
              if (x >=3 and x <=4) or (x >=9 and x <=11) or (x ==17) or (x ==23):
                 Tim.write(',225')
              else:
                 Tim.write(',' + str(coconuts[Ni]))
                 Ni = Ni + 1
           Tim.write('\n')
        else:
           Ni = 0
           for x in ModelPosition[:]:
              ModelPosition[Ni] = coconuts[Ni]
              minRange[Ni] = coconuts[Ni] - randFreedom
              maxRange[Ni] = coconuts[Ni] + randFreedom
              Ni = Ni + 1

           #parameters are model data, channels(17), population, mutation rate and maximum fitness value   
           GA = GAMotion(ModelPosition,17,PopulationSize,Mutation,FitMax)
           finalGeneration = GA.doRunOnce()
           if finalGeneration == maxGenerations:
              print "Total Generations:", finalGeneration
              print "Failed to find max fitness, try adjusting MaxFitness or other parameters" 
              sys.exit(0)
           else:
              print "Generations:"+ str(finalGeneration)
              print "GA Posture for motion file:" 
              coconuts = GA.getGAPosition()
              print coconuts   
           if n == 1:
              header = 'NO,DATA_NAME,SPEED,' + header
              Tim.write(header)
           Tim.write(str(n) + ',newMotion,' + str(speed))
           Ni = 0
           for x in range(0,24,1):
              if (x >=3 and x <=4) or (x >=9 and x <=11) or (x ==17) or (x ==23):
                 Tim.write(',225')
              else:
                 Tim.write(',' + str(coconuts[Ni]))
                 Ni = Ni + 1
           Tim.write('\n')

     Tim.close()

Virtual KHR-1

Making the robot walk.

====================================================================================

Installation of KHR1Sim simulator

====================================================================================

1. Download KHR1SimV2b.rar from
http://www.robosavvy.com/Builders/javi/

Extract the files to a suitable folder.

2. Download the XVL 3D plugin for Internet Explorer at:
http://www.lattice3d.com/products/products_player_3d_software.html

2.1 Click on Download Free XVL Players
2.2 Follow the link in the e-mail that you obtain. Download v.8 (NOT v.10!)*
2.3 Double-click on the XVL_Player_Pro-8_6bG.exe  file, and follow the
instructions.

To run the simulator, open the Virtual-KHR1.html page, using
(note!) Internet explorer (It does not work in Firefox).

  • The version of KHR1Sim used last year (v.1) worked only with
    XVL player v. 8. The new version (v.2b) might work with XVL
    player v. 10, but v. 8 is recommended (as it is known to work with KRH1SimV2b)

Using the instruction we were able to make a VIDEO of the action in progress.

References

  1. Q. Williams, S. Bogner, M. Kelley, C. Castillo, M. Lukac, D. H. Kim, J. Allen, M. Sunardi, S.Hossain, and M. Perkowski. “An Emotional Mimicking Humanoid Biped Robot and Its Quantum Control Based on the Constraint Satisfaction Model,” dwave.files.wordpress.com/2008/09/ulsi-2007-robot-1.doc –
  2. 2nd reference