You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pets.py 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. # Copyright (c) 2016 Anki, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License in the file LICENSE.txt or at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. '''Pet detection.
  15. Cozmo is capable of detecting pet faces (cats and dogs).
  16. The :class:`cozmo.world.World` object keeps track of pets the robot currently
  17. knows about, along with those that are currently visible to the camera.
  18. Each pet is assigned a :class:`Pet` object, which generates a number of
  19. observable events whenever the pet is observed, etc.
  20. If a pet goes off-screen, it will be assigned a new object_id (and
  21. therefore a new Pet object will be created) when it returns.
  22. This is because the system can only tell if something appears to be
  23. a cat or a dog; it cannot recognize a specific pet or, for instance,
  24. tell the difference between two dogs.
  25. Note that these pet-specific events are also passed up to the
  26. :class:`cozmo.world.World` object, so events for all pets can be
  27. observed by adding handlers there.
  28. '''
  29. # __all__ should order by constants, event classes, other classes, functions.
  30. __all__ = ['PET_VISIBILITY_TIMEOUT', 'PET_TYPE_CAT', 'PET_TYPE_DOG', 'PET_TYPE_UNKNOWN',
  31. 'EvtPetAppeared', 'EvtPetDisappeared', 'EvtPetObserved',
  32. 'Pet']
  33. import math
  34. import time
  35. from . import logger
  36. from . import event
  37. from . import objects
  38. from . import util
  39. from ._clad import _clad_to_game_anki
  40. #: Length of time in seconds to go without receiving an observed event before
  41. #: assuming that Cozmo can no longer see a pet.
  42. PET_VISIBILITY_TIMEOUT = objects.OBJECT_VISIBILITY_TIMEOUT
  43. # Pet types that Cozmo can distinguish
  44. #: Pet Type reported by Cozmo when unsure of type of pet
  45. PET_TYPE_UNKNOWN = "unknown"
  46. #: Pet Type reported by Cozmo when he thinks it's a cat
  47. PET_TYPE_CAT = "cat"
  48. #: Pet Type reported by Cozmo when he thinks it's a dog
  49. PET_TYPE_DOG = "dog"
  50. class EvtPetObserved(event.Event):
  51. '''Triggered whenever a pet is visually identified by the robot.
  52. A stream of these events are produced while a pet is visible to the robot.
  53. Each event has an updated image_box field.
  54. See EvtPetAppeared if you only want to know when a pet first
  55. becomes visible.
  56. '''
  57. pet = 'The Pet instance that was observed'
  58. updated = 'A set of field names that have changed'
  59. image_box = 'A comzo.util.ImageBox defining where the pet is within Cozmo\'s camera view'
  60. class EvtPetAppeared(event.Event):
  61. '''Triggered whenever a pet is first visually identified by a robot.
  62. This differs from EvtPetObserved in that it's only triggered when
  63. a pet initially becomes visible. If it disappears for more than
  64. PET_VISIBILITY_TIMEOUT seconds and then is seen again, a
  65. EvtPetDisappeared will be dispatched, followed by another
  66. EvtPetAppeared event.
  67. For continuous tracking information about a visible pet, see
  68. EvtPetObserved.
  69. '''
  70. pet = 'The Pet instance that was observed'
  71. updated = 'A set of field names that have changed'
  72. image_box = 'A comzo.util.ImageBox defining where the pet is within Cozmo\'s camera view'
  73. class EvtPetDisappeared(event.Event):
  74. '''Triggered whenever a pet that was previously being observed is no longer visible.'''
  75. pet = 'The Pet instance that is no longer being observed'
  76. def _clad_pet_type_to_pet_type(clad_pet_type):
  77. if clad_pet_type == _clad_to_game_anki.Vision.PetType.Unknown:
  78. return PET_TYPE_UNKNOWN
  79. elif clad_pet_type == _clad_to_game_anki.Vision.PetType.Cat:
  80. return PET_TYPE_CAT
  81. elif clad_pet_type == _clad_to_game_anki.Vision.PetType.Dog:
  82. return PET_TYPE_DOG
  83. else:
  84. raise ValueError("Unexpected pet type %s" % clad_pet_type)
  85. class Pet(objects.ObservableElement):
  86. '''A single pet that Cozmo has detected.
  87. See parent class :class:`~cozmo.objects.ObservableElement` for additional properties
  88. and methods.
  89. '''
  90. #: Length of time in seconds to go without receiving an observed event before
  91. #: assuming that Cozmo can no longer see a pet.
  92. visibility_timeout = PET_VISIBILITY_TIMEOUT
  93. def __init__(self, conn, world, robot, pet_id=None, **kw):
  94. super().__init__(conn, world, robot, **kw)
  95. self._pet_id = pet_id
  96. #: The type of Pet (PET_TYPE_CAT, PET_TYPE_DOG or PET_TYPE_UNKNOWN)
  97. self.pet_type = None
  98. def _repr_values(self):
  99. return 'pet_id=%s pet_type=%s' % (self.pet_id, self.pet_type)
  100. #### Private Methods ####
  101. def _dispatch_observed_event(self, changed_fields, image_box):
  102. self.dispatch_event(EvtPetObserved, pet=self,
  103. updated=changed_fields, image_box=image_box)
  104. def _dispatch_appeared_event(self, changed_fields, image_box):
  105. self.dispatch_event(EvtPetAppeared, pet=self,
  106. updated=changed_fields, image_box=image_box)
  107. def _dispatch_disappeared_event(self):
  108. self.dispatch_event(EvtPetDisappeared, pet=self)
  109. #### Properties ####
  110. @property
  111. def pet_id(self):
  112. '''int: The internal ID assigned to the pet.
  113. This value can only be assigned once as it is static in the engine.
  114. '''
  115. return self._pet_id
  116. @pet_id.setter
  117. def pet_id(self, value):
  118. if self._pet_id is not None:
  119. raise ValueError("Cannot change pet ID once set (from %s to %s)" % (self._pet_id, value))
  120. logger.debug("Updated pet_id for %s from %s to %s", self.__class__, self._pet_id, value)
  121. self._pet_id = value
  122. #### Private Event Handlers ####
  123. def _recv_msg_robot_observed_pet(self, evt, *, msg):
  124. changed_fields = set()
  125. pet_type = _clad_pet_type_to_pet_type(msg.petType)
  126. if pet_type != self.pet_type:
  127. self.pet_type = pet_type
  128. changed_fields.add('pet_type')
  129. image_box = util.ImageBox._create_from_clad_rect(msg.img_rect)
  130. self._on_observed(image_box, msg.timestamp, changed_fields)
  131. #### Public Event Handlers ####
  132. #### Event Wrappers ####
  133. #### Commands ####