123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939 |
- # Copyright (c) 2016-2017 Anki, Inc.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License in the file LICENSE.txt or at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
-
- '''Object and Power Cube recognition.
-
- Cozmo can recognize and track a number of different types of objects.
-
- These objects may be visible (currently observed by the robot's camera)
- and tappable (in the case of the Power Cubes that ship with the robot).
-
- Power Cubes are known as a :class:`LightCube` by the SDK. Each cube has
- controllable lights, and sensors that can determine when its being moved
- or tapped.
-
- Objects can emit several events such as :class:`EvtObjectObserved` when
- the robot sees (or continues to see) the object with its camera, or
- :class:`EvtObjectTapped` if a power cube is tapped by a player. You
- can either observe the object's instance directly, or capture all such events
- for all objects by observing them on :class:`cozmo.world.World` instead.
-
- All observable objects have a marker attached to them, which allows Cozmo
- to recognize the object and it's position and rotation("pose"). You can attach
- markers to your own objects for Cozmo to recognize by printing them out from the
- online documentation. They will be detected as :class:`CustomObject` instances.
- '''
-
-
- # __all__ should order by constants, event classes, other classes, functions.
- __all__ = ['LightCube1Id', 'LightCube2Id', 'LightCube3Id', 'LightCubeIDs',
- 'OBJECT_VISIBILITY_TIMEOUT',
- 'EvtObjectAppeared',
- 'EvtObjectConnectChanged', 'EvtObjectConnected',
- 'EvtObjectDisappeared', 'EvtObjectLocated',
- 'EvtObjectMoving', 'EvtObjectMovingStarted', 'EvtObjectMovingStopped',
- 'EvtObjectObserved', 'EvtObjectTapped',
- 'ObservableElement', 'ObservableObject', 'LightCube', 'Charger',
- 'CustomObject', 'CustomObjectMarkers', 'CustomObjectTypes', 'FixedCustomObject']
-
-
- import collections
- import math
- import time
-
- from . import logger
-
- from . import action
- from . import event
- from . import lights
- from . import util
-
- from ._clad import _clad_to_engine_iface, _clad_to_game_cozmo, _clad_to_engine_cozmo, _clad_to_game_anki
-
-
- #: Length of time in seconds to go without receiving an observed event before
- #: assuming that Cozmo can no longer see an object.
- OBJECT_VISIBILITY_TIMEOUT = 0.4
-
-
- class EvtObjectObserved(event.Event):
- '''Triggered whenever an object is visually identified by the robot.
-
- A stream of these events are produced while an object is visible to the robot.
- Each event has an updated image_box field.
-
- See EvtObjectAppeared if you only want to know when an object first
- becomes visible.
- '''
- obj = 'The object that was observed'
- updated = 'A set of field names that have changed'
- image_box = 'A comzo.util.ImageBox defining where the object is within Cozmo\'s camera view'
- pose = 'The cozmo.util.Pose defining the position and rotation of the object'
-
-
- class EvtObjectAppeared(event.Event):
- '''Triggered whenever an object is first visually identified by a robot.
-
- This differs from EvtObjectObserved in that it's only triggered when
- an object initially becomes visible. If it disappears for more than
- OBJECT_VISIBILITY_TIMEOUT seconds and then is seen again, a
- EvtObjectDisappeared will be dispatched, followed by another
- EvtObjectAppeared event.
-
- For continuous tracking information about a visible object, see
- EvtObjectObserved.
- '''
- obj = 'The object that was observed'
- updated = 'A set of field names that have changed'
- image_box = 'A comzo.util.ImageBox defining where the object is within Cozmo\'s camera view'
- pose = 'The cozmo.util.Pose defining the position and rotation of the object'
-
-
- class EvtObjectConnected(event.Event):
- '''Triggered when the engine reports that an object is connected (i.e. exists).
-
- This will usually occur at the start of the program in response to the SDK
- sending RequestConnectedObjects to the engine.
- '''
- obj = 'The object that is connected'
- connected = 'True if the object connected, False if it disconnected'
-
-
- class EvtObjectConnectChanged(event.Event):
- 'Triggered when an active object has connected or disconnected from the robot.'
- obj = 'The object that connected or disconnected'
- connected = 'True if the object connected, False if it disconnected'
-
-
- class EvtObjectLocated(event.Event):
- '''Triggered when the engine reports that an object is located (i.e. pose is known).
-
- This will usually occur at the start of the program in response to the SDK
- sending RequestLocatedObjectStates to the engine.
- '''
- obj = 'The object that is located'
- updated = 'A set of field names that have changed'
- pose = 'The cozmo.util.Pose defining the position and rotation of the object'
-
-
- class EvtObjectDisappeared(event.Event):
- '''Triggered whenever an object that was previously being observed is no longer visible.'''
- obj = 'The object that is no longer being observed'
-
- class EvtObjectMoving(event.Event):
- 'Triggered when an active object is currently moving.'
- obj = 'The object that is currently moving'
- # :class:`~cozmo.util.Vector3`: The currently measured acceleration
- acceleration = 'The currently measured acceleration'
- move_duration = 'The current duration of time (in seconds) that the object has spent moving'
-
- class EvtObjectMovingStarted(event.Event):
- 'Triggered when an active object starts moving.'
- obj = 'The object that started moving'
- #: :class:`~cozmo.util.Vector3`: The currently measured acceleration
- acceleration = 'The currently measured acceleration'
-
- class EvtObjectMovingStopped(event.Event):
- 'Triggered when an active object stops moving.'
- obj = 'The object that stopped moving'
- move_duration = 'The duration of time (in seconds) that the object spent moving'
-
- class EvtObjectTapped(event.Event):
- 'Triggered when an active object is tapped.'
- obj = 'The object that was tapped'
- tap_count = 'Number of taps detected'
- tap_duration = 'The duration of the tap in ms'
- tap_intensity = 'The intensity of the tap'
-
-
- class ObservableElement(event.Dispatcher):
- '''The base type for anything Cozmo can see.'''
-
- #: Length of time in seconds to go without receiving an observed event before
- #: assuming that Cozmo can no longer see an element. Can be overridden in sub
- #: classes.
- visibility_timeout = OBJECT_VISIBILITY_TIMEOUT
-
- def __init__(self, conn, world, robot, **kw):
- super().__init__(**kw)
- self._robot = robot
- self._pose = None
- self.conn = conn
- #: :class:`cozmo.world.World`: The robot's world in which this element is located.
- self.world = world
-
- #: float: The time the last event was received.
- #: ``None`` if no events have yet been received.
- self.last_event_time = None
-
- #: float: The time the element was last observed by the robot.
- #: ``None`` if the element has not yet been observed.
- self.last_observed_time = None
-
- #: int: The robot's timestamp of the last observed event.
- #: ``None`` if the element has not yet been observed.
- #: In milliseconds relative to robot epoch.
- self.last_observed_robot_timestamp = None
-
- #: :class:`~cozmo.util.ImageBox`: The ImageBox defining where the
- #: object was last visible within Cozmo's camera view.
- #: ``None`` if the element has not yet been observed.
- self.last_observed_image_box = None
-
- self._is_visible = False
- self._observed_timeout_handler = None
-
- def __repr__(self):
- extra = self._repr_values()
- if len(extra) > 0:
- extra = ' '+extra
- if self.pose:
- extra += ' pose=%s' % self.pose
-
- return '<%s%s is_visible=%s>' % (self.__class__.__name__,
- extra, self.is_visible)
-
- #### Private Methods ####
-
- def _repr_values(self):
- return ''
-
- def _update_field(self, changed, field_name, new_value):
- # Set only changed fields and update the passed in changed set
- current = getattr(self, field_name)
- if current != new_value:
- setattr(self, field_name, new_value)
- changed.add(field_name)
-
- def _reset_observed_timeout_handler(self):
- if self._observed_timeout_handler is not None:
- self._observed_timeout_handler.cancel()
- self._observed_timeout_handler = self._loop.call_later(
- self.visibility_timeout, self._observed_timeout)
-
- def _observed_timeout(self):
- # triggered when the element is no longer considered "visible"
- # ie. visibility_timeout seconds after the last observed event
- self._is_visible = False
- self._dispatch_disappeared_event()
-
- def _dispatch_observed_event(self, changed_fields, image_box):
- # Override in subclass if there is a specific event for that type
- pass
-
- def _dispatch_appeared_event(self, changed_fields, image_box):
- # Override in subclass if there is a specific event for that type
- pass
-
- def _dispatch_disappeared_event(self):
- # Override in subclass if there is a specific event for that type
- pass
-
- def _on_observed(self, image_box, timestamp, changed_fields):
- # Called from subclasses on their corresponding observed messages
- newly_visible = self._is_visible is False
- self._is_visible = True
-
- changed_fields |= {'last_observed_time', 'last_observed_robot_timestamp',
- 'last_event_time', 'last_observed_image_box'}
-
- now = time.time()
- self.last_observed_time = now
- self.last_observed_robot_timestamp = timestamp
- self.last_event_time = now
- self.last_observed_image_box = image_box
- self._reset_observed_timeout_handler()
- self._dispatch_observed_event(changed_fields, image_box)
-
- if newly_visible:
- self._dispatch_appeared_event(changed_fields, image_box)
-
- #### Properties ####
-
- @property
- def pose(self):
- ''':class:`cozmo.util.Pose`: The pose of the element in the world.
-
- Is ``None`` for elements that don't have pose information.
- '''
- return self._pose
-
- @property
- def time_since_last_seen(self):
- '''float: time since this element was last seen (math.inf if never)'''
- if self.last_observed_time is None:
- return math.inf
- return time.time() - self.last_observed_time
-
- @property
- def is_visible(self):
- '''bool: True if the element has been observed recently.
-
- "recently" is defined as :attr:`visibility_timeout` seconds.
- '''
- return self._is_visible
-
-
- class ObservableObject(ObservableElement):
- '''The base type for objects in Cozmo's world.
-
- See parent class :class:`ObservableElement` for additional properties
- and methods.
- '''
-
- #: bool: True if this type of object can be physically picked up by Cozmo
- pickupable = False
- #: bool: True if this type of object can have objects physically placed on it by Cozmo
- place_objects_on_this = False
-
- def __init__(self, conn, world, object_id=None, **kw):
- super().__init__(conn, world, robot=None, **kw)
- self._object_id = object_id
-
- #### Private Methods ####
-
- def _repr_values(self):
- return 'object_id=%s' % self.object_id
-
- def _dispatch_observed_event(self, changed_fields, image_box):
- self.dispatch_event(EvtObjectObserved, obj=self,
- updated=changed_fields, image_box=image_box, pose=self._pose)
-
- def _dispatch_appeared_event(self, changed_fields, image_box):
- self.dispatch_event(EvtObjectAppeared, obj=self,
- updated=changed_fields, image_box=image_box, pose=self._pose)
-
- def _dispatch_disappeared_event(self):
- self.dispatch_event(EvtObjectDisappeared, obj=self)
-
- def _handle_connected_object_state(self, object_state):
- # triggered when engine sends a ConnectedObjectStates message
- # as a response to a RequestConnectedObjects message
- self._pose = util.Pose._create_default()
- self.is_connected = True
- self.dispatch_event(EvtObjectConnected, obj=self)
-
- def _handle_located_object_state(self, object_state):
- # triggered when engine sends a LocatedObjectStates message
- # as a response to a RequestLocatedObjectStates message
- if (self.last_observed_robot_timestamp and
- (self.last_observed_robot_timestamp > object_state.lastObservedTimestamp)):
- logger.warning("Ignoring old located object_state=%s obj=%s (last_observed_robot_timestamp=%s)",
- object_state, self, self.last_observed_robot_timestamp)
- return
-
- changed_fields = {'last_observed_robot_timestamp', 'pose'}
-
- self.last_observed_robot_timestamp = object_state.lastObservedTimestamp
-
- self._pose = util.Pose._create_from_clad(object_state.pose)
- if object_state.poseState == _clad_to_game_anki.PoseState.Invalid:
- logger.error("Unexpected Invalid pose state received")
- self._pose.invalidate()
- elif object_state.poseState == _clad_to_game_anki.PoseState.Dirty:
- # Note Dirty currently means either moved (in which case it's really dirty)
- # or inaccurate (e.g. seen from too far away to give an accurate enough pose for localization)
- # TODO: split Dirty into 2 states, and allow SDK to report the distinction.
- self._pose._is_accurate = False
-
- self.dispatch_event(EvtObjectLocated,
- obj=self,
- updated=changed_fields,
- pose=self._pose)
-
- #### Properties ####
-
- @property
- def object_id(self):
- '''int: The internal ID assigned to the object.
-
- This value can only be assigned once as it is static in the engine.
- '''
- return self._object_id
-
- @object_id.setter
- def object_id(self, value):
- if self._object_id is not None:
- # We cannot currently rely on Engine ensuring that object ID remains static
- # E.g. in the case of a cube disconnecting and reconnecting it's removed
- # and then re-added to blockworld which results in a new ID.
- logger.warning("Changing object_id for %s from %s to %s", self.__class__, self._object_id, value)
- else:
- logger.debug("Setting object_id for %s to %s", self.__class__, value)
- self._object_id = value
-
- @property
- def descriptive_name(self):
- '''str: A descriptive name for this ObservableObject instance.'''
- # Note: Sub-classes should override this to add any other relevant info
- # for that object type.
- return "%s id=%d" % (self.__class__.__name__, self.object_id)
-
- #### Private Event Handlers ####
-
- def _recv_msg_robot_observed_object(self, evt, *, msg):
-
- changed_fields = {'pose'}
- self._pose = util.Pose._create_from_clad(msg.pose)
-
- image_box = util.ImageBox._create_from_clad_rect(msg.img_rect)
- self._on_observed(image_box, msg.timestamp, changed_fields)
-
- #### Public Event Handlers ####
-
- #### Event Wrappers ####
-
- #### Commands ####
-
-
- #: LightCube1Id's markers look a bit like a paperclip
- LightCube1Id = _clad_to_game_cozmo.ObjectType.Block_LIGHTCUBE1
- #: LightCube2Id's markers look a bit like a lamp (or a heart)
- LightCube2Id = _clad_to_game_cozmo.ObjectType.Block_LIGHTCUBE2
- #: LightCube3Id's markers look a bit like the letters 'ab' over 'T'
- LightCube3Id = _clad_to_game_cozmo.ObjectType.Block_LIGHTCUBE3
-
- #: An ordered list of the 3 light cube IDs for convenience
- LightCubeIDs = [LightCube1Id, LightCube2Id, LightCube3Id]
-
-
- class LightCube(ObservableObject):
- '''A light cube object has four LEDs that Cozmo can actively manipulate and communicate with.
-
- See parent class :class:`ObservableObject` for additional properties
- and methods.
- '''
- #TODO investigate why the top marker orientation of a cube is a bit strange
-
- #: Voltage where a cube's battery can be considered empty
- EMPTY_VOLTAGE = 1.0
- #: Voltage where a cube's battery can be considered full
- FULL_VOLTAGE = 1.5
-
- pickupable = True
- place_objects_on_this = True
-
- def __init__(self, cube_id, *a, **kw):
- super().__init__(*a, **kw)
-
- #: float: The time the object was last tapped
- #: ``None`` if the cube wasn't tapped yet.
- self.last_tapped_time = None
-
- #: int: The robot's timestamp of the last tapped event.
- #: ``None`` if the cube wasn't tapped yet.
- #: In milliseconds relative to robot epoch.
- self.last_tapped_robot_timestamp = None
-
- #: float: The time the object was last moved
- #: ``None`` if the cube wasn't moved yet.
- self.last_moved_time = None
-
- #: float: The time the object started moving when last moved
- self.last_moved_start_time = None
-
- #: int: The robot's timestamp of the last move event.
- #: ``None`` if the cube wasn't moved yet.
- #: In milliseconds relative to robot epoch.
- self.last_moved_robot_timestamp = None
-
- #: int: The robot's timestamp of when the object started moving when last moved
- #: ``None`` if the cube wasn't moved yet.
- #: In milliseconds relative to robot epoch.
- self.last_moved_start_robot_timestamp = None
-
- #: float: Battery voltage.
- #: ``None`` if no voltage reading has been received yet
- self.battery_voltage = None
-
- #: bool: True if the cube's accelerometer indicates that the cube is moving.
- self.is_moving = False
-
- #: bool: True if the cube is currently connected to the robot via radio.
- self.is_connected = False
-
- self._cube_id = cube_id
-
- def _repr_values(self):
- super_values = super()._repr_values()
- if len(super_values) > 0:
- super_values += ' '
- return ('{super_values}'
- 'battery={self.battery_str:s}'.format(self=self, super_values=super_values))
-
- #### Private Methods ####
-
- def _set_light(self, msg, idx, light):
- if not isinstance(light, lights.Light):
- raise TypeError("Expected a lights.Light")
- msg.onColor[idx] = light.on_color.int_color
- msg.offColor[idx] = light.off_color.int_color
- msg.onPeriod_ms[idx] = light.on_period_ms
- msg.offPeriod_ms[idx] = light.off_period_ms
- msg.transitionOnPeriod_ms[idx] = light.transition_on_period_ms
- msg.transitionOffPeriod_ms[idx] = light.transition_off_period_ms
-
-
- #### Event Wrappers ####
-
- async def wait_for_tap(self, timeout=None):
- '''Wait for the object to receive a tap event.
-
- Args:
- timeout (float): Maximum time to wait for a tap, in seconds. None for indefinite
- Returns:
- A :class:`EvtObjectTapped` object if a tap was received.
- '''
- return await self.wait_for(EvtObjectTapped, timeout=timeout)
-
-
- #### Properties ####
-
- @property
- def battery_percentage(self):
- """float: Battery level as a percentage."""
- if self.battery_voltage is None:
- # not received a voltage measurement yet
- return None
- elif self.battery_voltage >= self.FULL_VOLTAGE:
- return 100.0
- elif self.battery_voltage <= self.EMPTY_VOLTAGE:
- return 0.0
- else:
- return 100.0 * ((self.battery_voltage - self.EMPTY_VOLTAGE) /
- (self.FULL_VOLTAGE - self.EMPTY_VOLTAGE))
-
- @property
- def battery_str(self):
- """str: String representation of the battery level."""
- if self.battery_voltage is None:
- return "Unknown"
- else:
- return ('{self.battery_percentage:.0f}%'.format(self=self))
-
- @property
- def cube_id(self):
- """int: The Light Cube ID.
-
- This will be one of :attr:`~cozmo.objects.LightCube1Id`,
- :attr:`~cozmo.objects.LightCube2Id` and :attr:`~cozmo.objects.LightCube3Id`.
- Note: the cube_id is not the same thing as the object_id.
- """
- return self._cube_id
-
- #### Private Event Handlers ####
- def _recv_msg_object_tapped(self, evt, *, msg):
- now = time.time()
- self.last_event_time = now
- self.last_tapped_time = now
- self.last_tapped_robot_timestamp = msg.timestamp
- tap_intensity = msg.tapPos - msg.tapNeg
- self.dispatch_event(EvtObjectTapped, obj=self,
- tap_count=msg.numTaps, tap_duration=msg.tapTime, tap_intensity=tap_intensity)
-
- def _recv_msg_object_moved(self, evt, *, msg):
- now = time.time()
- started_moving = not self.is_moving
- self.is_moving = True
- self.last_event_time = now
- self.last_moved_time = now
- self.last_moved_robot_timestamp = msg.timestamp
-
- self.pose.invalidate()
-
- acceleration = util.Vector3(msg.accel.x, msg.accel.y, msg.accel.z)
-
- if started_moving:
- self.last_moved_start_time = now
- self.last_moved_start_robot_timestamp = msg.timestamp
- self.dispatch_event(EvtObjectMovingStarted, obj=self,
- acceleration=acceleration)
- else:
- move_duration = now - self.last_moved_start_time
- self.dispatch_event(EvtObjectMoving, obj=self,
- acceleration=acceleration,
- move_duration=move_duration)
-
- def _recv_msg_object_stopped_moving(self, evt, *, msg):
- now = time.time()
- if self.is_moving:
- self.is_moving = False
- move_duration = now - self.last_moved_start_time
- else:
- # This happens for very short movements that are immediately
- # considered stopped (no acceleration info is present)
- move_duration = 0.0
- self.dispatch_event(EvtObjectMovingStopped, obj=self,
- move_duration=move_duration)
-
- def _recv_msg_object_power_level(self, evt, *, msg):
- self.battery_voltage = msg.batteryLevel * 0.01
-
- def _recv_msg_object_connection_state(self, evt, *, msg):
- if self.is_connected != msg.connected:
- if msg.connected:
- logger.info("Object connected: %s", self)
- else:
- logger.info("Object disconnected: %s", self)
- self.is_connected = msg.connected
- self.dispatch_event(EvtObjectConnectChanged, obj=self,
- connected=self.is_connected)
-
- @property
- def descriptive_name(self):
- '''str: A descriptive name for this LightCube instance.'''
- # Specialization of ObservableObject's method to include the cube ID.
- return "%s %s id=%d" % (self.__class__.__name__, self._cube_id, self.object_id)
-
- #### Public Event Handlers ####
-
- def recv_evt_object_tapped(self, evt, **kw):
- pass
-
- #### Commands ####
-
- # TODO: make this explicit as to which light goes to which corner.
- def set_light_corners(self, light1, light2, light3, light4):
- """Set the light for each corner"""
- msg = _clad_to_engine_iface.SetAllActiveObjectLEDs(objectID=self.object_id)
- for i, light in enumerate( (light1, light2, light3, light4) ):
- if light is not None:
- lights._set_light(msg, i, light)
-
- self.conn.send_msg(msg)
-
- def set_lights(self, light):
- '''Set all lights on the cube
-
- Args:
- light (:class:`cozmo.lights.Light`): The settings for the lights.
- '''
- msg = _clad_to_engine_iface.SetAllActiveObjectLEDs(
- objectID=self.object_id)
- for i in range(4):
- lights._set_light(msg, i, light)
-
- self.conn.send_msg(msg)
-
- def set_lights_off(self):
- '''Turn off all the lights on the cube.'''
- self.set_lights(lights.off_light)
-
-
- class Charger(ObservableObject):
- '''Cozmo's charger object, which the robot can observe and drive toward.
-
- See parent class :class:`ObservableObject` for additional properties
- and methods.
- '''
-
- def __init__(self, *a, **kw):
- super().__init__(*a, **kw)
-
-
- class CustomObject(ObservableObject):
- '''An object defined by the SDK. It is bound to a specific objectType e.g ``CustomType00``.
-
- This defined object is given a size in the x,y and z axis. The dimensions
- of the markers on the object are also defined. We get an
- :class:`cozmo.objects.EvtObjectObserved` message when the robot sees these
- markers.
-
- See parent class :class:`ObservableObject` for additional properties
- and methods.
-
- These objects are created automatically by the engine when Cozmo observes
- an object with custom markers. For Cozmo to see one of these you must first
- define an object with custom markers, via one of the following methods:
- :meth:`~cozmo.world.World.define_custom_box`.
- :meth:`~cozmo.world.World.define_custom_cube`, or
- :meth:`~cozmo.world.World.define_custom_wall`
- '''
-
- def __init__(self, conn, world, object_type,
- x_size_mm, y_size_mm, z_size_mm,
- marker_width_mm, marker_height_mm, is_unique, **kw):
- super().__init__(conn, world, **kw)
-
- self.object_type = object_type
- self._x_size_mm = x_size_mm
- self._y_size_mm = y_size_mm
- self._z_size_mm = z_size_mm
- self._marker_width_mm = marker_width_mm
- self._marker_height_mm = marker_height_mm
- self._is_unique = is_unique
-
- def _repr_values(self):
- return ('object_type={self.object_type} '
- 'x_size_mm={self.x_size_mm:.1f} '
- 'y_size_mm={self.y_size_mm:.1f} '
- 'z_size_mm={self.z_size_mm:.1f} '
- 'is_unique={self.is_unique}'.format(self=self))
-
- #### Private Methods ####
-
- #### Event Wrappers ####
- #### Properties ####
- @property
- def x_size_mm(self):
- '''float: Size of this object in its X axis, in millimeters.'''
- return self._x_size_mm
-
- @property
- def y_size_mm(self):
- '''float: Size of this object in its Y axis, in millimeters.'''
- return self._y_size_mm
-
- @property
- def z_size_mm(self):
- '''float: Size of this object in its Z axis, in millimeters.'''
- return self._z_size_mm
-
- @property
- def marker_width_mm(self):
- '''float: Width in millimeters of the marker on this object.'''
- return self._marker_width_mm
-
- @property
- def marker_height_mm(self):
- '''float: Height in millimeters of the marker on this object.'''
- return self._marker_height_mm
-
- @property
- def is_unique(self):
- '''bool: True if there should only be one of this object type in the world.'''
- return self._is_unique
-
- @property
- def descriptive_name(self):
- '''str: A descriptive name for this CustomObject instance.'''
- # Specialization of ObservableObject's method to include the object type.
- return "%s id=%d" % (self.object_type.name, self.object_id)
-
- #### Private Event Handlers ####
-
- #### Public Event Handlers ####
-
- #### Commands ####
-
-
- class _CustomObjectType(collections.namedtuple('_CustomObjectType', 'name id')):
- # Tuple mapping between CLAD ActionResult name and ID
- # All instances will be members of ActionResults
-
- # Keep _ActionResult as lightweight as a normal namedtuple
- __slots__ = ()
-
- def __str__(self):
- return 'CustomObjectTypes.%s' % self.name
-
-
- class CustomObjectTypes:
- '''Defines all available custom object types.
-
- For use with world.define_custom methods such as
- :meth:`cozmo.world.World.define_custom_box`,
- :meth:`cozmo.world.World.define_custom_cube`, and
- :meth:`cozmo.world.World.define_custom_wall`
- '''
-
- #: CustomType00 - the first custom object type
- CustomType00 = _CustomObjectType("CustomType00", _clad_to_engine_cozmo.ObjectType.CustomType00)
-
- #:
- CustomType01 = _CustomObjectType("CustomType01", _clad_to_engine_cozmo.ObjectType.CustomType01)
-
- #:
- CustomType02 = _CustomObjectType("CustomType02", _clad_to_engine_cozmo.ObjectType.CustomType02)
-
- #:
- CustomType03 = _CustomObjectType("CustomType03", _clad_to_engine_cozmo.ObjectType.CustomType03)
-
- #:
- CustomType04 = _CustomObjectType("CustomType04", _clad_to_engine_cozmo.ObjectType.CustomType04)
-
- #:
- CustomType05 = _CustomObjectType("CustomType05", _clad_to_engine_cozmo.ObjectType.CustomType05)
-
- #:
- CustomType06 = _CustomObjectType("CustomType06", _clad_to_engine_cozmo.ObjectType.CustomType06)
-
- #:
- CustomType07 = _CustomObjectType("CustomType07", _clad_to_engine_cozmo.ObjectType.CustomType07)
-
- #:
- CustomType08 = _CustomObjectType("CustomType08", _clad_to_engine_cozmo.ObjectType.CustomType08)
-
- #:
- CustomType09 = _CustomObjectType("CustomType09", _clad_to_engine_cozmo.ObjectType.CustomType09)
-
- #:
- CustomType10 = _CustomObjectType("CustomType10", _clad_to_engine_cozmo.ObjectType.CustomType10)
-
- #:
- CustomType11 = _CustomObjectType("CustomType11", _clad_to_engine_cozmo.ObjectType.CustomType11)
-
- #:
- CustomType12 = _CustomObjectType("CustomType12", _clad_to_engine_cozmo.ObjectType.CustomType12)
-
- #:
- CustomType13 = _CustomObjectType("CustomType13", _clad_to_engine_cozmo.ObjectType.CustomType13)
-
- #:
- CustomType14 = _CustomObjectType("CustomType14", _clad_to_engine_cozmo.ObjectType.CustomType14)
-
- #:
- CustomType15 = _CustomObjectType("CustomType15", _clad_to_engine_cozmo.ObjectType.CustomType15)
-
- #:
- CustomType16 = _CustomObjectType("CustomType16", _clad_to_engine_cozmo.ObjectType.CustomType16)
-
- #:
- CustomType17 = _CustomObjectType("CustomType17", _clad_to_engine_cozmo.ObjectType.CustomType17)
-
- #:
- CustomType18 = _CustomObjectType("CustomType18", _clad_to_engine_cozmo.ObjectType.CustomType18)
-
- #: CustomType19 - the last custom object type
- CustomType19 = _CustomObjectType("CustomType19", _clad_to_engine_cozmo.ObjectType.CustomType19)
-
-
- _CustomObjectMarker = collections.namedtuple('_CustomObjectMarker', 'name id')
-
- class CustomObjectMarkers:
- '''Defines all available custom object markers.
-
- For use with world.define_custom methods such as
- :meth:`cozmo.world.World.define_custom_box`,
- :meth:`cozmo.world.World.define_custom_cube`, and
- :meth:`cozmo.world.World.define_custom_wall`
- '''
-
- #: .. image:: ../images/custom_markers/SDK_2Circles.png
- Circles2 = _CustomObjectMarker("Circles2", _clad_to_engine_cozmo.CustomObjectMarker.Circles2)
-
- #: .. image:: ../images/custom_markers/SDK_3Circles.png
- Circles3 = _CustomObjectMarker("Circles3", _clad_to_engine_cozmo.CustomObjectMarker.Circles3)
-
- #: .. image:: ../images/custom_markers/SDK_4Circles.png
- Circles4 = _CustomObjectMarker("Circles4", _clad_to_engine_cozmo.CustomObjectMarker.Circles4)
-
- #: .. image:: ../images/custom_markers/SDK_5Circles.png
- Circles5 = _CustomObjectMarker("Circles5", _clad_to_engine_cozmo.CustomObjectMarker.Circles5)
-
- #: .. image:: ../images/custom_markers/SDK_2Diamonds.png
- Diamonds2 = _CustomObjectMarker("Diamonds2", _clad_to_engine_cozmo.CustomObjectMarker.Diamonds2)
-
- #: .. image:: ../images/custom_markers/SDK_3Diamonds.png
- Diamonds3 = _CustomObjectMarker("Diamonds3", _clad_to_engine_cozmo.CustomObjectMarker.Diamonds3)
-
- #: .. image:: ../images/custom_markers/SDK_4Diamonds.png
- Diamonds4 = _CustomObjectMarker("Diamonds4", _clad_to_engine_cozmo.CustomObjectMarker.Diamonds4)
-
- #: .. image:: ../images/custom_markers/SDK_5Diamonds.png
- Diamonds5 = _CustomObjectMarker("Diamonds5", _clad_to_engine_cozmo.CustomObjectMarker.Diamonds5)
-
- #: .. image:: ../images/custom_markers/SDK_2Hexagons.png
- Hexagons2 = _CustomObjectMarker("Hexagons2", _clad_to_engine_cozmo.CustomObjectMarker.Hexagons2)
-
- #: .. image:: ../images/custom_markers/SDK_3Hexagons.png
- Hexagons3 = _CustomObjectMarker("Hexagons3", _clad_to_engine_cozmo.CustomObjectMarker.Hexagons3)
-
- #: .. image:: ../images/custom_markers/SDK_4Hexagons.png
- Hexagons4 = _CustomObjectMarker("Hexagons4", _clad_to_engine_cozmo.CustomObjectMarker.Hexagons4)
-
- #: .. image:: ../images/custom_markers/SDK_5Hexagons.png
- Hexagons5 = _CustomObjectMarker("Hexagons5", _clad_to_engine_cozmo.CustomObjectMarker.Hexagons5)
-
- #: .. image:: ../images/custom_markers/SDK_2Triangles.png
- Triangles2 = _CustomObjectMarker("Triangles2", _clad_to_engine_cozmo.CustomObjectMarker.Triangles2)
-
- #: .. image:: ../images/custom_markers/SDK_3Triangles.png
- Triangles3 = _CustomObjectMarker("Triangles3", _clad_to_engine_cozmo.CustomObjectMarker.Triangles3)
-
- #: .. image:: ../images/custom_markers/SDK_4Triangles.png
- Triangles4 = _CustomObjectMarker("Triangles4", _clad_to_engine_cozmo.CustomObjectMarker.Triangles4)
-
- #: .. image:: ../images/custom_markers/SDK_5Triangles.png
- Triangles5 = _CustomObjectMarker("Triangles5", _clad_to_engine_cozmo.CustomObjectMarker.Triangles5)
-
-
- class FixedCustomObject():
- '''A fixed object defined by the SDK. It is given a pose and x,y,z sizes.
-
- This object cannot be observed by the robot so its pose never changes.
- The position is static in Cozmo's world view; once instantiated, these
- objects never move. This could be used to make Cozmo aware of objects and
- know to plot a path around them even when they don't have any markers.
-
- To create these use :meth:`~cozmo.world.World.create_custom_fixed_object`
- '''
-
- is_visible = False
-
- def __init__(self, pose, x_size_mm, y_size_mm, z_size_mm, object_id, *a, **kw):
- super().__init__(*a, **kw)
- self._pose = pose
- self._object_id = object_id
- self._x_size_mm = x_size_mm
- self._y_size_mm = y_size_mm
- self._z_size_mm = z_size_mm
-
- def __repr__(self):
- return ('<%s pose=%s object_id=%d x_size_mm=%.1f y_size_mm=%.1f z_size_mm=%.1f=>' %
- (self.__class__.__name__, self.pose, self.object_id,
- self.x_size_mm, self.y_size_mm, self.z_size_mm))
-
- #### Private Methods ####
- #### Event Wrappers ####
- #### Properties ####
- @property
- def object_id(self):
- '''int: The internal ID assigned to the object.
-
- This value can only be assigned once as it is static in the engine.
- '''
- return self._object_id
-
- @object_id.setter
- def object_id(self, value):
- if self._object_id is not None:
- raise ValueError("Cannot change object ID once set (from %s to %s)" % (self._object_id, value))
- logger.debug("Updated object_id for %s from %s to %s", self.__class__, self._object_id, value)
- self._object_id = value
-
- @property
- def pose(self):
- ''':class:`cozmo.util.Pose`: The pose of the object in the world.'''
- return self._pose
-
- @property
- def x_size_mm(self):
- '''float: The length of the object in its X axis, in millimeters.'''
- return self._x_size_mm
-
- @property
- def y_size_mm(self):
- '''float: The length of the object in its Y axis, in millimeters.'''
- return self._y_size_mm
-
- @property
- def z_size_mm(self):
- '''float: The length of the object in its Z axis, in millimeters.'''
- return self._z_size_mm
-
-
- #### Private Event Handlers ####
- #### Public Event Handlers ####
- #### Commands ####
|