1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335 |
- # 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.
-
- '''Classes and functions relating to an individual Cozmo robot.
-
- The :meth:`cozmo.conn.CozmoConnection.wait_for_robot` method returns an
- instance of :class:`Robot` which controls a single Cozmo robot.
-
- The :class:`Robot` class has methods and properties to determine its current
- state, control its low-level motors, play animations and start behaviors as
- well as performing high-level actions such as detecting faces and picking up
- objects.
-
- Each :class:`Robot` has a :attr:`Robot.world` attribute which represents an
- instance of a :class:`cozmo.world.World`. This tracks the state of the world
- that Cozmo knows about: The objects and faces it's currently observing,
- the camera images it's receiving, etc. You can monitor the world instance for
- various events that occur, or monitor individual objects directly: The
- world instance receives all events that the robot triggers, and nearly all SDK
- objects inherit from :class:`cozmo.event.Dispatcher` and therefore inherit
- methods such as :meth:`~cozmo.event.Dispatcher.wait_for` and
- :meth:`~cozmo.event.Dispatcher.add_event_handler`.
- '''
-
- # __all__ should order by constants, event classes, other classes, functions.
- __all__ = ['MIN_HEAD_ANGLE', 'MAX_HEAD_ANGLE',
- 'MIN_LIFT_HEIGHT', 'MIN_LIFT_HEIGHT_MM', 'MAX_LIFT_HEIGHT', 'MAX_LIFT_HEIGHT_MM',
- 'MIN_LIFT_ANGLE', 'MAX_LIFT_ANGLE',
- # Event classes
- 'EvtRobotReady', 'EvtRobotStateUpdated', 'EvtUnexpectedMovement',
- # Helper classes
- 'LiftPosition', 'UnexpectedMovementSide', 'UnexpectedMovementType',
- # Robot Action classes
- 'DisplayOledFaceImage', 'DockWithCube', 'DriveOffChargerContacts', 'DriveStraight',
- 'GoToObject', 'GoToPose', 'PerformOffChargerContext', 'PickupObject',
- 'PlaceObjectOnGroundHere', 'PlaceOnObject', 'PopAWheelie', 'RollCube', 'SayText',
- 'SetHeadAngle', 'SetLiftHeight', 'TurnInPlace', 'TurnTowardsFace',
- # Robot
- 'Robot']
-
-
- import asyncio
- import collections
- import math
- import warnings
-
- from . import logger, logger_protocol
- from . import action
- from . import anim
- from . import audio
- from . import song
- from . import behavior
- from . import camera
- from . import conn
- from . import event
- from . import exceptions
- from . import lights
- from . import objects
- from . import util
- from . import world
- from . import robot_alignment
-
- from ._clad import _clad_to_engine_iface, _clad_to_engine_cozmo, _clad_to_engine_anki, _clad_to_game_cozmo, CladEnumWrapper
-
- #### Events
-
- class EvtRobotReady(event.Event):
- '''Generated when the robot has been initialized and is ready for commands.'''
- robot = "Robot object representing the robot to command"
-
-
- class EvtRobotStateUpdated(event.Event):
- '''Dispatched whenever the robot's state is updated (multiple times per second).'''
- robot = "Robot object representing the robot to command"
-
-
- class EvtUnexpectedMovement(event.Event):
- '''Triggered whenever the robot does not move as expected (typically rotation).'''
- robot = "Robot object representing the robot to command"
- timestamp = "Robot timestamp for when the unexpected movement occurred"
- movement_type = "An UnexpectedMovementType Object representing the type of unexpected movement"
- movement_side = "An UnexpectedMovementSide Object representing the side that is obstructing movement"
-
- #### Constants
-
-
- #: The minimum angle the robot's head can be set to
- MIN_HEAD_ANGLE = util.degrees(-25)
-
- #: The maximum angle the robot's head can be set to
- MAX_HEAD_ANGLE = util.degrees(44.5)
-
- # The lowest height-above-ground that lift can be moved to in millimeters.
- MIN_LIFT_HEIGHT_MM = 32.0
-
- #: The lowest height-above-ground that lift can be moved to
- MIN_LIFT_HEIGHT = util.distance_mm(MIN_LIFT_HEIGHT_MM)
-
- # The largest height-above-ground that lift can be moved to in millimeters.
- MAX_LIFT_HEIGHT_MM = 92.0
-
- #: The largest height-above-ground that lift can be moved to
- MAX_LIFT_HEIGHT = util.distance_mm(MAX_LIFT_HEIGHT_MM)
-
- #: The length of Cozmo's lift arm
- LIFT_ARM_LENGTH = util.distance_mm(66.0)
-
- #: The height above ground of Cozmo's lift arm's pivot
- LIFT_PIVOT_HEIGHT = util.distance_mm(45.0)
-
- #: The minimum angle the robot's lift can be set to
- MIN_LIFT_ANGLE = util.radians(math.asin((MIN_LIFT_HEIGHT_MM - LIFT_PIVOT_HEIGHT.distance_mm) / LIFT_ARM_LENGTH.distance_mm))
-
- #: The maximum angle the robot's lift can be set to
- MAX_LIFT_ANGLE = util.radians(math.asin((MAX_LIFT_HEIGHT_MM - LIFT_PIVOT_HEIGHT.distance_mm) / LIFT_ARM_LENGTH.distance_mm))
-
-
- class LiftPosition:
- '''Represents the position of Cozmo's lift.
-
- The class allows the position to be referred to as either absolute height
- above the ground, as a ratio from 0.0 to 1.0, or as the angle of the lift
- arm relative to the ground.
-
- Args:
- height (:class:`cozmo.util.Distance`): The height of the lift above the ground.
- ratio (float): The ratio from 0.0 to 1.0 that the lift is raised from the ground.
- angle (:class:`cozmo.util.Angle`): The angle of the lift arm relative to the ground.
- '''
- __slots__ = ('_height')
-
- def __init__(self, height=None, ratio=None, angle=None):
- def _count_arg(arg):
- # return 1 if argument is set (not None), 0 otherwise
- return 0 if (arg is None) else 1
- num_provided_args = _count_arg(height) + _count_arg(ratio) + _count_arg(angle)
- if num_provided_args != 1:
- raise ValueError("Expected one, and only one, of the distance, ratio or angle keyword arguments")
-
- if height is not None:
- if not isinstance(height, util.Distance):
- raise TypeError("Unsupported type for distance - expected util.Distance")
- self._height = height
- elif ratio is not None:
- height_mm = MIN_LIFT_HEIGHT_MM + (ratio * (MAX_LIFT_HEIGHT_MM - MIN_LIFT_HEIGHT_MM))
- self._height = util.distance_mm(height_mm)
- elif angle is not None:
- if not isinstance(angle, util.Angle):
- raise TypeError("Unsupported type for angle - expected util.Angle")
- height_mm = (math.sin(angle.radians) * LIFT_ARM_LENGTH.distance_mm) + LIFT_PIVOT_HEIGHT.distance_mm
- self._height = util.distance_mm(height_mm)
-
- def __repr__(self):
- return "<%s height=%s ratio=%s angle=%s>" % (self.__class__.__name__, self._height, self.ratio, self.angle)
-
- @property
- def height(self):
- ''':class:`cozmo.util.Distance`: The height above the ground.'''
- return self._height
-
- @property
- def ratio(self):
- '''float: The ratio from 0 to 1 that the lift is raised, 0 at the bottom, 1 at the top.'''
- ratio = ((self._height.distance_mm - MIN_LIFT_HEIGHT_MM) /
- (MAX_LIFT_HEIGHT_MM - MIN_LIFT_HEIGHT_MM))
- return ratio
-
- @property
- def angle(self):
- ''':class:`cozmo.util.Angle`: The angle of the lift arm relative to the ground.'''
- sin_angle = (self._height.distance_mm - LIFT_PIVOT_HEIGHT.distance_mm) / LIFT_ARM_LENGTH.distance_mm
- angle_radians = math.asin(sin_angle)
- return util.radians(angle_radians)
-
-
- #### Actions
-
- class GoToPose(action.Action):
- '''Represents the go to pose action in progress.
-
- Returned by :meth:`~cozmo.robot.Robot.go_to_pose`
- '''
- def __init__(self, pose, **kw):
- super().__init__(**kw)
- self.pose = pose
-
- def _repr_values(self):
- return "pose=%s" % (self.pose)
-
- def _encode(self):
- return _clad_to_engine_iface.GotoPose(x_mm=self.pose.position.x,
- y_mm=self.pose.position.y,
- rad=self.pose.rotation.angle_z.radians)
-
-
- class GoToObject(action.Action):
- '''Represents the go to object action in progress.
-
- Returned by :meth:`~cozmo.robot.Robot.go_to_object`
- '''
- def __init__(self, object_id, distance_from_object, **kw):
- super().__init__(**kw)
- self.object_id = object_id
- self.distance_from_object = distance_from_object
-
- def _repr_values(self):
- return "object_id=%s, distance_from_object=%s" % (self.object_id, self.distance_from_object)
-
- def _encode(self):
- return _clad_to_engine_iface.GotoObject(objectID=self.object_id,
- distanceFromObjectOrigin_mm=self.distance_from_object.distance_mm,
- useManualSpeed=False,
- usePreDockPose=False)
-
- class DockWithCube(action.Action):
- '''Represents the dock with cube action in progress.
-
- Returned by :meth:`~cozmo.robot.Robot.dock_with_cube`
- '''
- def __init__(self, obj, approach_angle, alignment_type, distance_from_marker, **kw):
- super().__init__(**kw)
- #: The object (e.g. an instance of :class:`cozmo.objects.LightCube`) that is being put down
- self.obj = obj
- self.alignment_type = alignment_type
- if approach_angle is None:
- self.use_approach_angle = False
- self.approach_angle = util.degrees(0)
- else:
- self.use_approach_angle = True
- self.approach_angle = approach_angle
-
- if distance_from_marker is None:
- self.distance_from_marker = util.distance_mm(0)
- else:
- self.distance_from_marker = distance_from_marker
-
- def _repr_values(self):
- return "object=%s" % (self.obj)
-
- def _encode(self):
- return _clad_to_engine_iface.AlignWithObject(objectID=self.obj.object_id,
- distanceFromMarker_mm=self.distance_from_marker.distance_mm,
- approachAngle_rad=self.approach_angle.radians,
- alignmentType=self.alignment_type.id,
- useApproachAngle=self.use_approach_angle,
- usePreDockPose=self.use_approach_angle,
- useManualSpeed=False)
-
- class RollCube(action.Action):
- '''Represents the roll cube action in progress.
-
- Returned by :meth:`~cozmo.robot.Robot.roll_cube`
- '''
- def __init__(self, obj, approach_angle, check_for_object_on_top, **kw):
- super().__init__(**kw)
- #: The object (e.g. an instance of :class:`cozmo.objects.LightCube`) that is being put down
- self.obj = obj
- #: bool: whether to check if there is an object on top
- self.check_for_object_on_top = check_for_object_on_top
- if approach_angle is None:
- self.use_approach_angle = False
- self.approach_angle = util.degrees(0)
- else:
- self.use_approach_angle = True
- self.approach_angle = approach_angle
-
- def _repr_values(self):
- return "object=%s, check_for_object_on_top=%s, approach_angle=%s" % (self.obj, self.check_for_object_on_top, self.approach_angle)
-
- def _encode(self):
- return _clad_to_engine_iface.RollObject(objectID=self.obj.object_id,
- approachAngle_rad=self.approach_angle.radians,
- useApproachAngle=self.use_approach_angle,
- usePreDockPose=self.use_approach_angle,
- useManualSpeed=False,
- checkForObjectOnTop=self.check_for_object_on_top)
-
- class DriveOffChargerContacts(action.Action):
- '''Represents the drive off charger contacts action in progress.
-
- Returned by :meth:`~cozmo.robot.Robot.drive_off_charger_contacts`
- '''
- def __init__(self, **kw):
- super().__init__(**kw)
-
- def _repr_values(self):
- return ""
-
- def _encode(self):
- return _clad_to_engine_iface.DriveOffChargerContacts()
-
-
- class DriveStraight(action.Action):
- '''Represents the "drive straight" action in progress.
-
- Returned by :meth:`~cozmo.robot.Robot.drive_straight`
- '''
- def __init__(self, distance, speed, should_play_anim, **kw):
- super().__init__(**kw)
- #: :class:`cozmo.util.Distance`: The distance to drive
- self.distance = distance
- #: :class:`cozmo.util.Speed`: The speed to drive at
- self.speed = speed
- #: bool: Whether to play an animation whilst driving
- self.should_play_anim = should_play_anim
-
- def _repr_values(self):
- return "distance=%s speed=%s should_play_anim=%s" % (self.distance, self.speed, self.should_play_anim)
-
- def _encode(self):
- return _clad_to_engine_iface.DriveStraight(speed_mmps=self.speed.speed_mmps,
- dist_mm=self.distance.distance_mm,
- shouldPlayAnimation=self.should_play_anim)
-
-
- class DisplayOledFaceImage(action.Action):
- '''Represents the "display oled face image" action in progress.
-
- Returned by :meth:`~cozmo.robot.Robot.display_oled_face_image`
- '''
- # Face images are sent so frequently, with the previous face image always
- # aborted, that logging each event would spam the log.
- _enable_abort_logging = False
-
- def __init__(self, screen_data, duration_ms, **kw):
- super().__init__(**kw)
- #: :class:`bytes`: a sequence of pixels (8 pixels per byte)
- self.screen_data = screen_data
- #: float: time to keep displaying this image on Cozmo's face
- self.duration_ms = duration_ms
-
- def _repr_values(self):
- return "screen_data=%s Bytes duration_ms=%s" %\
- (len(self.screen_data), self.duration_ms)
-
- def _encode(self):
- return _clad_to_engine_iface.DisplayFaceImage(faceData=self.screen_data,
- duration_ms=self.duration_ms)
-
-
- class PickupObject(action.Action):
- '''Represents the pickup object action in progress.
-
- Returned by :meth:`~cozmo.robot.Robot.pickup_object`
- '''
-
- def __init__(self, obj, use_pre_dock_pose=True, **kw):
- super().__init__(**kw)
- #: The object (e.g. an instance of :class:`cozmo.objects.LightCube`) that was picked up
- self.obj = obj
- #: A bool that is true when Cozmo needs to go to a pose before attempting to navigate to the object
- self.use_pre_dock_pose = use_pre_dock_pose
-
- def _repr_values(self):
- return "object=%s" % (self.obj,)
-
- def _encode(self):
- return _clad_to_engine_iface.PickupObject(objectID=self.obj.object_id,
- usePreDockPose=self.use_pre_dock_pose)
-
-
- class PlaceOnObject(action.Action):
- '''Tracks the state of the "place on object" action.
-
- return by :meth:`~cozmo.robot.Robot.place_on_object`
- '''
- def __init__(self, obj, use_pre_dock_pose=True, **kw):
- super().__init__(**kw)
- #: The object (e.g. an instance of :class:`cozmo.objects.LightCube`) that the held object will be placed on
- self.obj = obj
- #: A bool that is true when Cozmo needs to go to a pose before attempting to navigate to the object
- self.use_pre_dock_pose = use_pre_dock_pose
-
- def _repr_values(self):
- return "object=%s use_pre_dock_pose=%s" % (self.obj, self.use_pre_dock_pose)
-
- def _encode(self):
- return _clad_to_engine_iface.PlaceOnObject(objectID=self.obj.object_id,
- usePreDockPose=self.use_pre_dock_pose)
-
-
- class PlaceObjectOnGroundHere(action.Action):
- '''Tracks the state of the "place object on ground here" action.
-
- Returned by :meth:`~cozmo.robot.Robot.place_object_on_ground_here`
- '''
- def __init__(self, obj, **kw):
- super().__init__(**kw)
- #: The object (e.g. an instance of :class:`cozmo.objects.LightCube`) that is being put down
- self.obj = obj
-
- def _repr_values(self):
- return "object=%s" % (self.obj,)
-
- def _encode(self):
- return _clad_to_engine_iface.PlaceObjectOnGroundHere()
-
-
- class SayText(action.Action):
- '''Tracks the progress of a say text robot action.
-
- Returned by :meth:`~cozmo.robot.Robot.say_text`
- '''
- def __init__(self, text, play_excited_animation, use_cozmo_voice, duration_scalar, voice_pitch, **kw):
- super().__init__(**kw)
- self.text = text
-
- # Note: play_event must be an AnimationTrigger that supports text-to-speech being generated
-
- if play_excited_animation:
- self.play_event = _clad_to_engine_cozmo.AnimationTrigger.OnSawNewNamedFace
- else:
- # TODO: Switch to use AnimationTrigger.SdkTextToSpeech when that works correctly
- self.play_event = _clad_to_engine_cozmo.AnimationTrigger.Count
-
- if use_cozmo_voice:
- self.say_style = _clad_to_engine_cozmo.SayTextVoiceStyle.CozmoProcessing_Sentence
- else:
- # default male human voice
- self.say_style = _clad_to_engine_cozmo.SayTextVoiceStyle.Unprocessed
-
- self.duration_scalar = duration_scalar
- self.voice_pitch = voice_pitch
-
- def _repr_values(self):
- return "text='%s' style=%s event=%s duration=%s pitch=%s" %\
- (self.text, self.say_style, self.play_event, self.duration_scalar, self.voice_pitch)
-
- def _encode(self):
- return _clad_to_engine_iface.SayText(text=self.text,
- playEvent=self.play_event,
- voiceStyle=self.say_style,
- durationScalar=self.duration_scalar,
- voicePitch=self.voice_pitch,
- fitToDuration=False)
-
-
- class SetHeadAngle(action.Action):
- '''Represents the Set Head Angle action in progress.
- Returned by :meth:`~cozmo.robot.Robot.set_head_angle`
- '''
- def __init__(self, angle, max_speed, accel, duration, warn_on_clamp, **kw):
- super().__init__(**kw)
-
- if angle < MIN_HEAD_ANGLE:
- if warn_on_clamp:
- logger.warning("Clamping head angle from %s to min %s" % (angle, MIN_HEAD_ANGLE))
- self.angle = MIN_HEAD_ANGLE
- elif angle > MAX_HEAD_ANGLE:
- if warn_on_clamp:
- logger.warning("Clamping head angle from %s to max %s" % (angle, MAX_HEAD_ANGLE))
- self.angle = MAX_HEAD_ANGLE
- else:
- self.angle = angle
-
- #: float: Maximum speed of Cozmo's head in radians per second
- self.max_speed = max_speed
-
- #: float: Acceleration of Cozmo's head in radians per second squared
- self.accel = accel
-
- #: float: Time for Cozmo's head to turn in seconds
- self.duration = duration
-
- def _repr_values(self):
- return "angle=%s max_speed=%s accel=%s duration=%s" %\
- (self.angle, self.max_speed, self.accel, self.duration)
-
- def _encode(self):
- return _clad_to_engine_iface.SetHeadAngle(angle_rad=self.angle.radians,
- max_speed_rad_per_sec=self.max_speed,
- accel_rad_per_sec2=self.accel,
- duration_sec=self.duration)
-
-
- class SetLiftHeight(action.Action):
- '''Represents the Set Lift Height action in progress.
- Returned by :meth:`~cozmo.robot.Robot.set_lift_height`
- '''
- def __init__(self, height, max_speed, accel, duration, **kw):
- super().__init__(**kw)
-
- if height < 0.0:
- logger.warning("lift height %s too small, should be in 0..1 range - clamping", height)
- self.lift_height_mm = MIN_LIFT_HEIGHT_MM
- elif height > 1.0:
- logger.warning("lift height %s too large, should be in 0..1 range - clamping", height)
- self.lift_height_mm = MAX_LIFT_HEIGHT_MM
- else:
- self.lift_height_mm = MIN_LIFT_HEIGHT_MM + (height * (MAX_LIFT_HEIGHT_MM - MIN_LIFT_HEIGHT_MM))
-
- #: float: Maximum speed of Cozmo's lift in radians per second
- self.max_speed = max_speed
-
- #: float: Acceleration of Cozmo's lift in radians per second squared
- self.accel = accel
-
- #: float: Time for Cozmo's lift to turn in seconds
- self.duration = duration
-
- def _repr_values(self):
- return "height=%s max_speed=%s accel=%s duration=%s" %\
- (self.lift_height_mm, self.max_speed, self.accel, self.duration)
-
- def _encode(self):
- return _clad_to_engine_iface.SetLiftHeight(height_mm=self.lift_height_mm,
- max_speed_rad_per_sec=self.max_speed,
- accel_rad_per_sec2=self.accel,
- duration_sec=self.duration)
-
-
- class TurnInPlace(action.Action):
- '''Tracks the progress of a turn in place robot action.
-
- Returned by :meth:`~cozmo.robot.Robot.turn_in_place`
- '''
- def __init__(self, angle, speed, accel, angle_tolerance, is_absolute, **kw):
- super().__init__(**kw)
- #: :class:`cozmo.util.Angle`: The angle to turn
- self.angle = angle
- #: :class:`cozmo.util.Angle`: Angular turn speed (per second).
- self.speed = speed
- #: :class:`cozmo.util.Angle`: Acceleration of angular turn (per second squared).
- self.accel = accel
- #: :class:`cozmo.util.Angle`: The minimum angular tolerance to consider
- #: the action complete (this is clamped to a minimum of 2 degrees internally).
- self.angle_tolerance = angle_tolerance
- #: bool: True to turn to a specific angle, False to turn relative to the current pose.
- self.is_absolute = is_absolute
-
- def _repr_values(self):
- return "angle=%s, speed=%s, accel=%s, tolerance=%s is_absolute=%s" %\
- (self.angle, self.speed, self.accel, self.angle_tolerance, self.is_absolute)
-
- def _get_radians(self, in_angle, default_value=0.0):
- # Helper method to allow None angles to represent default values
- if in_angle is None:
- return default_value
- else:
- return in_angle.radians
-
- def _encode(self):
- return _clad_to_engine_iface.TurnInPlace(
- angle_rad = self.angle.radians,
- speed_rad_per_sec = self._get_radians(self.speed),
- accel_rad_per_sec2 = self._get_radians(self.accel),
- tol_rad = self._get_radians(self.angle_tolerance),
- isAbsolute = int(self.is_absolute))
-
-
- class PopAWheelie(action.Action):
- '''Tracks the progress of a "pop a wheelie" robot action.
-
- Returned by :meth:`~cozmo.robot.Robot.pop_a_wheelie`
- '''
- def __init__(self, obj, approach_angle, **kw):
- super().__init__(**kw)
- #: An object (e.g. an instance of :class:`cozmo.objects.LightCube`)
- #: being used as leverage to push cozmo on his back
- self.obj = obj
- if approach_angle is None:
- self.use_approach_angle = False
- self.approach_angle = util.degrees(0)
- else:
- self.use_approach_angle = True
- self.approach_angle = approach_angle
-
- def _repr_values(self):
- return ("object=%s, use_approach_angle=%s, approach_angle=%s" %
- (self.obj, self.use_approach_angle, self.approach_angle) )
-
- def _encode(self):
- return _clad_to_engine_iface.PopAWheelie(
- objectID=self.obj.object_id,
- approachAngle_rad=self.approach_angle.radians,
- useApproachAngle=self.use_approach_angle,
- usePreDockPose=self.use_approach_angle,
- useManualSpeed=False)
-
-
- class TurnTowardsFace(action.Action):
- '''Tracks the progress of a turn towards face robot action.
-
- Returned by :meth:`~cozmo.robot.Robot.turn_towards_face`
- '''
- def __init__(self, face, **kw):
- super().__init__(**kw)
- #: :class:`~cozmo.faces.Face`: The face to turn towards
- self.face = face
-
- def _repr_values(self):
- return "face=%s" % (self.face)
-
- def _encode(self):
- return _clad_to_engine_iface.TurnTowardsFace(
- faceID=self.face.face_id,
- maxTurnAngle_rad=util.degrees(180).radians)
-
-
-
- class PerformOffChargerContext(event.Dispatcher):
- '''A helper class to provide a context manager to do operations while Cozmo is off charger.'''
- def __init__(self, robot, **kw):
- super().__init__(**kw)
- self.robot = robot
-
- async def __aenter__(self):
- self.was_on_charger = self.robot.is_on_charger
- if self.was_on_charger:
- await self.robot.drive_off_charger_contacts(in_parallel=True).wait_for_completed()
- return self
-
- async def __aexit__(self, exc_type, exc_value, traceback):
- if self.was_on_charger:
- await self.robot.backup_onto_charger()
- return False
-
-
- class Robot(event.Dispatcher):
- """The interface to a Cozmo robot.
-
- A robot has access to:
-
- * A :class:`~cozmo.world.World` object (:attr:`cozmo.robot.Robot.world`),
- which tracks the state of the world the robot knows about
-
- * A :class:`~cozmo.camera.Camera` object (:attr:`cozmo.robot.Robot.camera`),
- which provides access to Cozmo's camera
-
- * An Animations object, controlling the playing of animations on the robot
-
- * A Behaviors object, starting and ending robot behaviors such as looking around
-
- Robots are instantiated by the :class:`~cozmo.conn.CozmoConnection` object
- and emit a :class:`EvtRobotReady` when it has been configured and is
- ready to be commanded.
- """
-
- # action factories
- _action_dispatcher_factory = action._ActionDispatcher
-
- #: callable: The factory function that returns a
- #: :class:`TurnInPlace` class or subclass instance.
- turn_in_place_factory = TurnInPlace
-
- #: callable: The factory function that returns a
- #: :class:`TurnTowardsFace` class or subclass instance.
- turn_towards_face_factory = TurnTowardsFace
-
- #: callable: The factory function that returns a
- #: :class:`PickupObject` class or subclass instance.
- pickup_object_factory = PickupObject
-
- #: callable: The factory function that returns a
- #: :class:`PlaceOnObject` class or subclass instance.
- place_on_object_factory = PlaceOnObject
-
- #: callable: The factory function that returns a
- #: :class:`GoToPose` class or subclass instance.
- go_to_pose_factory = GoToPose
-
- #: callable: The factory function that returns a
- #: :class:`GoToObject` class or subclass instance.
- go_to_object_factory = GoToObject
-
- #: callable: The factory function that returns a
- #: :class:`DockWithCube` class or subclass instance.
- dock_with_cube_factory = DockWithCube
-
- #: callable: The factory function that returns a
- #: :class:`RollCube` class or subclass instance.
- roll_cube_factory = RollCube
-
- #: callable: The factory function that returns a
- #: :class:`PlaceObjectOnGroundHere` class or subclass instance.
- place_object_on_ground_here_factory = PlaceObjectOnGroundHere
-
- #: callable: The factory function that returns a
- #: :class:`PopAWheelie` class or subclass instance.
- pop_a_wheelie_factory = PopAWheelie
-
- #: callable: The factory function that returns a
- #: :class:`SayText` class or subclass instance.
- say_text_factory = SayText
-
- #: callable: The factory function that returns a
- #: :class:`SetHeadAngle` class or subclass instance.
- set_head_angle_factory = SetHeadAngle
-
- #: callable: The factory function that returns a
- #: :class:`SetLiftHeight` class or subclass instance.
- set_lift_height_factory = SetLiftHeight
-
- #: callable: The factory function that returns a
- #: :class:`DriveOffChargerContacts` class or subclass instance.
- drive_off_charger_contacts_factory = DriveOffChargerContacts
-
- #: callable: The factory function that returns a
- #: :class:`DriveStraight` class or subclass instance.
- drive_straight_factory = DriveStraight
-
- #: callable: The factory function that returns a
- #: :class:`DisplayOledFaceImage` class or subclass instance.
- display_oled_face_image_factory = DisplayOledFaceImage
-
- # other factories
-
- #: callable: The factory function that returns a
- #: :class:`cozmo.anim.Animation` class or subclass instance.
- animation_factory = anim.Animation
-
- #: callable: The factory function that returns a
- #: :class:`cozmo.anim.AnimationTrigger` class or subclass instance.
- animation_trigger_factory = anim.AnimationTrigger
-
- #: callable: The factory function that returns a
- #: :class:`cozmo.behavior.Behavior` class or subclass instance.
- behavior_factory = behavior.Behavior
-
- #: callable: The factory function that returns a
- #: :class:`cozmo.camera.Camera` class or subclass instance.
- camera_factory = camera.Camera
-
- #: callable: The factory function that returns a
- #: :class:`cozmo.robot.PerformOffChargerContext` class or subclass instance.
- perform_off_charger_factory = PerformOffChargerContext
-
- #: callable: The factory function that returns a
- #: :class:`cozmo.world.World` class or subclass instance.
- world_factory = world.World
-
- # other attributes
-
- #: bool: Set to True if the robot should drive off the charger as soon
- #: as the SDK connects to the engine. Defaults to True.
- drive_off_charger_on_connect = True # Required for most movement actions
-
- _current_behavior = None # type: Behavior
- _is_freeplay_mode_active = False
-
- def __init__(self, conn, robot_id: int, is_primary: bool, **kw):
- super().__init__(**kw)
- #: :class:`cozmo.conn.CozmoConnection`: The active connection to the engine.
- self.conn = conn # type: conn.CozmoConnection
- #: int: The internal ID number of the robot.
- self.robot_id = robot_id
-
- self._is_ready = False
- self._pose = None # type: util.Pose
- #: bool: Specifies that this is the primary robot (always True currently)
- self.is_primary = is_primary
-
- #: :class:`cozmo.camera.Camera`: Provides access to the robot's camera
- self.camera = self.camera_factory(self, dispatch_parent=self)
-
- #: :class:`cozmo.world.World`: Tracks state information about Cozmo's world.
- self.world = self.world_factory(self.conn, self, dispatch_parent=self)
-
- self._action_dispatcher = self._action_dispatcher_factory(self)
-
- self._current_face_image_action = None # type: DisplayOledFaceImage
-
- #: :class:`cozmo.util.Speed`: Speed of the left wheel
- self.left_wheel_speed = None # type: util.Speed
- #: :class:`cozmo.util.Speed`: Speed of the right wheel
- self.right_wheel_speed = None # type: util.Speed
- self._lift_position = LiftPosition(height=util.distance_mm(MIN_LIFT_HEIGHT_MM))
- #: float: The current battery voltage (not linear, but < 3.5 is low)
- self.battery_voltage = None # type: float
-
- #: :class:`cozmo.util.Vector3`: The current accelerometer reading (x,y,z)
- #: In mm/s^2, measured in Cozmo's head (e.g. x=0 when Cozmo's head is level
- #: but x = z = ~7000 mm/s^2 when Cozmo's head is angled 45 degrees up)
- self.accelerometer = None # type: util.Vector3
-
- self._is_device_accelerometer_supported = None # type: bool
- self._is_device_gyro_supported = None # type: bool
-
- #: :class:`cozmo.util.Vector3`: The current accelerometer reading for
- #: the connected mobile device. Requires that you have first called
- #: :meth:`enable_device_imu` with `enable_raw = True`. See
- #: :attr:`device_accel_user` for a user-filtered equivalent.
- self.device_accel_raw = None # type: util.Vector3
-
- #: :class:`cozmo.util.Vector3`: The current user-filtered accelerometer
- #: reading for the connected mobile device. Requires that you have first
- #: called :meth:`enable_device_imu` with `enable_user = True`. This
- #: filtered version removes the constant acceleration from Gravity. See
- #: :attr:`device_accel_raw` for a raw version.
- self.device_accel_user = None # type: util.Vector3
-
- #: :class:`cozmo.util.Quaternion`: The current gyro reading for
- #: the connected mobile device. Requires that you have first called
- #: :meth:`enable_device_imu` with `enable_gyro = True`
- self.device_gyro = None # type: util.Quaternion
-
- #: :class:`cozmo.util.Vector3`: The current gyro reading (x,y,z)
- #: In radians/s, measured in Cozmo's head.
- #: Therefore a large value in a given component would indicate Cozmo is
- #: being rotated around that axis (where x=forward, y=left, z=up), e.g.
- #: y = -5 would indicate that Cozmo is being rolled onto his back
- self.gyro = None # type: util.Vector3
-
- #: int: The ID of the object currently being carried (-1 if none)
- self.carrying_object_id = -1
- #: int: The ID of the object on top of the object currently being carried (-1 if none)
- self.carrying_object_on_top_id = -1
- #: int: The ID of the object the head is tracking to (-1 if none)
- self.head_tracking_object_id = -1
- #: int: The ID of the object that the robot is localized to (-1 if none)
- self.localized_to_object_id = -1
- #: int: The robot's timestamp for the last image seen.
- #: ``None`` if no image was received yet.
- #: In milliseconds relative to robot epoch.
- self.last_image_robot_timestamp = None # type: int
- self._pose_angle = None # type: util.Angle
- self._pose_pitch = None # type: util.Angle
- self._head_angle = None # type: util.Angle
- self._robot_status_flags = 0
- self._game_status_flags = 0
-
- self._serial_number_head = 0
- self._serial_number_body = 0
- self._model_number = 0
- self._hw_version = 0
-
- # send all received events to the world and action dispatcher
- self._add_child_dispatcher(self._action_dispatcher)
- self._add_child_dispatcher(self.world)
-
-
- #### Private Methods ####
-
- def _initialize(self):
- # Perform all steps necessary to initialize the robot and trigger
- # an EvtRobotReady event when complete.
- async def _init():
- # Note: Robot state is reset on entering SDK mode, and after any SDK program exits
- self.stop_all_motors()
- self.enable_all_reaction_triggers(False)
- self.enable_stop_on_cliff(True)
- self._set_none_behavior()
-
- # Default to no memory map data being streamed
- self.world.request_nav_memory_map(-1.0)
-
- # Default to no device IMU data being streamed
- self.enable_device_imu(False, False, False)
-
- # Ensure the SDK has full control of cube lights
- self._set_cube_light_state(False)
-
- await self.world.delete_all_custom_objects()
-
- # wait for animations to load
- await self.conn.anim_names.wait_for_loaded()
-
- msg = _clad_to_engine_iface.GetBlockPoolMessage()
- self.conn.send_msg(msg)
-
- self._is_ready = True
- logger.info('Robot id=%s serial=%s initialized OK', self.robot_id, self.serial)
- self.dispatch_event(EvtRobotReady, robot=self)
-
- self._idle_stack_depth = 0
- self.set_idle_animation(anim.Triggers.Count)
- asyncio.ensure_future(_init(), loop=self._loop)
-
- def _set_none_behavior(self):
- # Internal helper method called from Behavior.stop etc.
- msg = _clad_to_engine_iface.ExecuteBehaviorByExecutableType(
- behaviorType=_clad_to_engine_cozmo.ExecutableBehaviorType.Wait)
- self.conn.send_msg(msg)
- if self._current_behavior is not None:
- self._current_behavior._set_stopped()
-
- def _set_cube_light_state(self, enable):
- msg = _clad_to_engine_iface.EnableLightStates(enable=enable, objectID=-1)
- self.conn.send_msg(msg)
-
- def _enable_cube_sleep(self, enable=True, skip_animation=True):
- # skip_animation (bool): True to skip the fadeout part of the sleep anim
- msg = _clad_to_engine_iface.EnableCubeSleep(enable=enable,
- skipAnimation=skip_animation)
- self.conn.send_msg(msg)
-
- #### Properties ####
-
- @property
- def is_ready(self):
- """bool: True if the robot has been initialized and is ready to accept commands."""
- return self._is_ready
-
- @property
- def anim_names(self):
- '''set of string: Set of all the available animation names
-
- An alias of :attr:`cozmo.conn.anim_names`.
-
- Generally animation triggers are preferred over explict animation names:
- See :class:`cozmo.anim.Triggers` for available animation triggers.
- '''
- return self.conn.anim_names
-
- @property
- def anim_triggers(self):
- '''list of :class:`cozmo.anim.Triggers`, specifying available animation triggers
-
- These can be sent to the play_anim_trigger to make the robot perform animations.
-
- An alias of :attr:`cozmo.anim.Triggers.trigger_list`.
- '''
- return anim.Triggers.trigger_list
-
- @property
- def pose(self):
- """:class:`cozmo.util.Pose`: The current pose (position and orientation) of Cozmo
- """
- return self._pose
-
- @property
- def is_moving(self):
- '''bool: True if Cozmo is currently moving anything (head, lift or wheels/treads).'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_MOVING) != 0
-
- @property
- def is_carrying_block(self):
- '''bool: True if Cozmo is currently carrying a block.'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_CARRYING_BLOCK) != 0
-
- @property
- def is_picking_or_placing(self):
- '''bool: True if Cozmo is picking or placing something.'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_PICKING_OR_PLACING) != 0
-
- @property
- def is_picked_up(self):
- '''bool: True if Cozmo is currently picked up (in the air).'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_PICKED_UP) != 0
-
- @property
- def is_falling(self):
- '''bool: True if Cozmo is currently falling.'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_FALLING) != 0
-
- @property
- def is_animating(self):
- '''bool: True if Cozmo is currently playing an animation.'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_ANIMATING) != 0
-
- @property
- def is_animating_idle(self):
- '''bool: True if Cozmo is currently playing an idle animation.'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_ANIMATING_IDLE) != 0
-
- @property
- def is_pathing(self):
- '''bool: True if Cozmo is currently traversing a path.'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_PATHING) != 0
-
- @property
- def is_lift_in_pos(self):
- '''bool: True if Cozmo's lift is in the desired position (False if still trying to move there).'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.LIFT_IN_POS) != 0
-
- @property
- def is_head_in_pos(self):
- '''bool: True if Cozmo's head is in the desired position (False if still trying to move there).'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.HEAD_IN_POS) != 0
-
- @property
- def is_anim_buffer_full(self):
- '''bool: True if Cozmo's animation buffer is full (on robot).'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_ANIM_BUFFER_FULL) != 0
-
- @property
- def is_on_charger(self):
- '''bool: True if Cozmo is currently on the charger.'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_ON_CHARGER) != 0
-
- @property
- def is_charging(self):
- '''bool: True if Cozmo is currently charging.'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_CHARGING) != 0
-
- @property
- def is_cliff_detected(self):
- '''bool: True if Cozmo detected a cliff (in front of the robot).'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.CLIFF_DETECTED) != 0
-
- @property
- def are_wheels_moving(self):
- '''bool: True if Cozmo's wheels/treads are currently moving.'''
- return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.ARE_WHEELS_MOVING) != 0
-
- @property
- def is_localized(self):
- '''bool: True if Cozmo is localized (i.e. knows where he is with respect to a cube, and has both treads on the ground).'''
- return (self._game_status_flags & _clad_to_game_cozmo.GameStatusFlag.IsLocalized) != 0
-
- @property
- def pose_angle(self):
- ''':class:`cozmo.util.Angle`: Cozmo's pose angle (heading in X-Y plane).'''
- return self._pose_angle
-
- @property
- def pose_pitch(self):
- ''':class:`cozmo.util.Angle`: Cozmo's pose pitch (angle up/down).'''
- return self._pose_pitch
-
- @property
- def head_angle(self):
- ''':class:`cozmo.util.Angle`: Cozmo's head angle (up/down).'''
- return self._head_angle
-
- @property
- def lift_position(self):
- ''':class:`LiftPosition`: The position of Cozmo's lift.'''
- return self._lift_position
-
- @property
- def lift_height(self):
- ''':class:`cozmo.util.Distance`: Height of Cozmo's lift from the ground.
-
- In :const:`MIN_LIFT_HEIGHT` to :const:`MAX_LIFT_HEIGHT` range.
- '''
- return self._lift_position.height
-
- @property
- def lift_ratio(self):
- '''float: Ratio from 0 to 1 of how high Cozmo's lift is.'''
- return self._lift_position.ratio
-
- @property
- def lift_angle(self):
- ''':class:`cozmo.util.Angle`: Angle of Cozmo's lift relative to the ground.
-
- In :const:`MIN_LIFT_ANGLE` to :const:`MAX_LIFT_ANGLE` range.
- '''
- return self._lift_position.angle
-
- @property
- def current_behavior(self):
- ''':class:`cozmo.behavior.Behavior`: Cozmo's currently active behavior.'''
- if self._current_behavior is not None and self._current_behavior.is_active:
- return self._current_behavior
- else:
- return None
-
- @property
- def is_behavior_running(self):
- '''bool: True if Cozmo is currently running a behavior.
-
- When Cozmo is running a behavior he will behave fairly autonomously
- (playing animations and other actions as desired). Attempting to drive
- Cozmo whilst in this mode will likely have unexpected behavior on
- the robot and confuse Cozmo.
- '''
- return (self.is_freeplay_mode_active or
- (self._current_behavior is not None and self._current_behavior.is_active))
-
- @property
- def is_freeplay_mode_active(self):
- '''bool: True if Cozmo is in freeplay mode.
-
- When Cozmo is in freeplay mode he will behave autonomously (playing
- behaviors, animations and other actions as desired). Attempting to
- drive Cozmo whilst in this mode will likely have unexpected behavior
- on the robot and confuse Cozmo.
- '''
- return self._is_freeplay_mode_active
-
- @property
- def has_in_progress_actions(self):
- '''bool: True if Cozmo has any SDK-triggered actions still in progress.'''
- return self._action_dispatcher.has_in_progress_actions
-
- @property
- def camera_config(self):
- ''':class:`cozmo.robot.CameraConfig`: The read-only config/calibration for this robot's camera
-
- .. deprecated:: 0.12.0
- Use: :meth:`cozmo.camera.Camera.config` instead.
- '''
- warnings.warn("The 'robot.camera_config' method is deprecated, "
- "use 'robot.camera.config' instead", DeprecationWarning, stacklevel=2)
- return self.camera.config
-
- @property
- def serial(self):
- '''string: The serial number, as a hex-string (e.g "02e08032"), for the robot.
-
- This matches the Cozmo Serial value in the About section of the settings
- menu in the app.
- '''
- return "%08x" % self._serial_number_body
-
- @property
- def is_device_accelerometer_supported(self):
- """bool: True if the attached mobile device supports accelerometer data."""
- return self._is_device_accelerometer_supported
-
- @property
- def is_device_gyro_supported(self):
- """bool: True if the attached mobile device supports gyro data."""
- return self._is_device_gyro_supported
-
- #### Private Event Handlers ####
-
- #def _recv_default_handler(self, event, **kw):
- # msg = kw.get('msg')
- # logger_protocol.debug("Robot received unhandled internal event_name=%s kw=%s", event.event_name, kw)
-
- def recv_default_handler(self, event, **kw):
- logger.debug("Robot received unhandled public event=%s", event)
-
- def _recv_msg_processed_image(self, _, *, msg):
- pass
-
- def _recv_msg_image_chunk(self, evt, *, msg):
- self.camera.dispatch_event(evt)
-
- def _recv_msg_current_camera_params(self, evt, *, msg):
- self.camera.dispatch_event(evt)
-
- def _recv_msg_robot_observed_motion(self, evt, *, msg):
- self.camera.dispatch_event(evt)
-
- def _recv_msg_per_robot_settings(self, evt, *, msg):
- self._serial_number_head = msg.serialNumberHead
- self._serial_number_body = msg.serialNumberBody
- self._model_number = msg.modelNumber
- self._hw_version = msg.hwVersion
- self.camera._set_config(msg.cameraConfig)
-
- def _recv_msg_unexpected_movement(self, evt, *, msg):
- movement_type = UnexpectedMovementType.find_by_id(msg.movementType)
- movement_side = UnexpectedMovementSide.find_by_id(msg.movementSide)
- self.dispatch_event(EvtUnexpectedMovement, robot=self, timestamp=msg.timestamp,
- movement_type=movement_type, movement_side=movement_side)
-
- def _recv_msg_robot_state(self, evt, *, msg):
- self._pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z,
- q0=msg.pose.q0, q1=msg.pose.q1,
- q2=msg.pose.q2, q3=msg.pose.q3,
- origin_id=msg.pose.originID)
- self._pose_angle = util.radians(msg.poseAngle_rad) # heading in X-Y plane
- self._pose_pitch = util.radians(msg.posePitch_rad)
- self._head_angle = util.radians(msg.headAngle_rad)
- self.left_wheel_speed = util.speed_mmps(msg.leftWheelSpeed_mmps)
- self.right_wheel_speed = util.speed_mmps(msg.rightWheelSpeed_mmps)
- self._lift_position = LiftPosition(height=util.distance_mm(msg.liftHeight_mm))
- self.battery_voltage = msg.batteryVoltage
- self.accelerometer = util.Vector3(msg.accel.x, msg.accel.y, msg.accel.z)
- self.gyro = util.Vector3(msg.gyro.x, msg.gyro.y, msg.gyro.z)
- self.carrying_object_id = msg.carryingObjectID # int_32 will be -1 if not carrying object
- self.carrying_object_on_top_id = msg.carryingObjectOnTopID # int_32 will be -1 if no object on top of object being carried
- self.head_tracking_object_id = msg.headTrackingObjectID # int_32 will be -1 if head is not tracking to any object
- self.localized_to_object_id = msg.localizedToObjectID # int_32 Will be -1 if not localized to any object
- self.last_image_robot_timestamp = msg.lastImageTimeStamp
- self._robot_status_flags = msg.status # uint_16 as bitflags - See _clad_to_game_cozmo.RobotStatusFlag
- self._game_status_flags = msg.gameStatus # uint_8 as bitflags - See _clad_to_game_cozmo.GameStatusFlag
-
- self.dispatch_event(EvtRobotStateUpdated, robot=self)
-
- def _recv_msg_behavior_transition(self, evt, *, msg):
- new_type = behavior.BehaviorTypes.find_by_id(msg.newBehaviorExecType)
- if self._current_behavior is not None:
- if new_type == self._current_behavior.type:
- self._current_behavior._on_engine_started()
- else:
- self._current_behavior._set_stopped()
-
- # Device IMU
-
- def _recv_msg_device_accelerometer_values_raw(self, evt, *, msg):
- self.device_accel_raw = util.Vector3(msg.x_gForce, msg.y_gForce, msg.z_gForce)
-
- def _recv_msg_device_accelerometer_values_user(self, evt, *, msg):
- self.device_accel_user = util.Vector3(msg.x_gForce, msg.y_gForce, msg.z_gForce)
-
- def _recv_msg_device_gyro_values(self, evt, *, msg):
- self.device_gyro = util.Quaternion(msg.w, msg.x, msg.y, msg.z)
-
- def _recv_msg_is_device_imu_supported(self, evt, *, msg):
- self._is_device_accelerometer_supported = msg.isAccelerometerSupported
- self._is_device_gyro_supported = msg.isGyroSupported
- logger.debug("Mobile Device IMU support: accelerometer=%s gyro=%s",
- self._is_device_accelerometer_supported, self._is_device_gyro_supported)
-
- #### Public Event Handlers ####
-
-
- #### Commands ####
-
- def enable_all_reaction_triggers(self, should_enable):
- '''Enable or disable Cozmo's responses to being handled or observing the world.
-
- Args:
- should_enable (bool): True if the robot should react to its environment.
- '''
- if should_enable:
- msg = _clad_to_engine_iface.RemoveDisableReactionsLock("sdk")
- self.conn.send_msg(msg)
- else:
- msg = _clad_to_engine_iface.DisableAllReactionsWithLock("sdk")
- self.conn.send_msg(msg)
-
- def enable_stop_on_cliff(self, enable):
- '''Enable or disable Cozmo's ability to drive off a cliff.
-
- Args:
- enable (bool): True if the robot should stop moving when a cliff is encountered.
- '''
- msg = _clad_to_engine_iface.EnableStopOnCliff(enable=enable)
- self.conn.send_msg(msg)
-
- def set_robot_volume(self, robot_volume):
- '''Set the volume for the speaker in the robot.
-
- Args:
- robot_volume (float): The new volume (0.0 = mute, 1.0 = max).
- '''
- msg = _clad_to_engine_iface.SetRobotVolume(robotId=self.robot_id, volume=robot_volume)
- self.conn.send_msg(msg)
-
- def abort_all_actions(self, log_abort_messages=False):
- '''Abort all actions on this robot
-
- Args:
- log_abort_messages (bool): True to log info on every action that
- is aborted.
-
- Abort / Cancel any action that is currently either running or queued within the engine
- '''
- self._action_dispatcher._abort_all_actions(log_abort_messages)
-
- def enable_facial_expression_estimation(self, enable=True):
- '''Enable or Disable facial expression estimation
-
- Cozmo can optionally estimate the facial expression for human faces to
- see if he thinks they're happy, sad, etc.
-
- Args:
- enable (bool): True to enable facial expression estimation, False to
- disable it. By default Cozmo starts with it disabled to save on
- processing time.
- '''
-
- msg = _clad_to_engine_iface.EnableVisionMode(
- mode=_clad_to_engine_cozmo.VisionMode.EstimatingFacialExpression,
- enable=enable)
- self.conn.send_msg(msg)
-
- def enable_device_imu(self, enable_raw=False, enable_user=False, enable_gyro=False):
- """Enable streaming of the connected Mobile devices' IMU data.
-
- The accelerometer and gyro data for the connected phone or tablet can
- be streamed from the app to the SDK. You can request any combination of
- the 3 data types.
-
- Args:
- enable_raw (bool): True to enable streaming of the raw accelerometer
- data, which can be accessed via :attr:`device_accel_raw`
- enable_user (bool): True to enable streaming of the user-filtered
- accelerometer data, which can be accessed via :attr:`device_accel_user`
- enable_gyro (bool): True to enable streaming of the gyro
- data, which can be accessed via :attr:`device_gyro`
- """
- msg = _clad_to_engine_iface.EnableDeviceIMUData(enableAccelerometerRaw=enable_raw,
- enableAccelerometerUser=enable_user,
- enableGyro=enable_gyro)
- self.conn.send_msg(msg)
-
- def set_needs_levels(self, repair_value=1, energy_value=1, play_value=1):
- """Manually set Cozmo's current needs levels.
-
- The needs levels control whether Cozmo needs repairing, feeding or playing with.
- Values outside of the 0.0 to 1.0 range are clamped internally.
-
- Args:
- repair_value (float): How repaired is Cozmo - 0='broken', 1='fully repaired'
- energy_value (float): How energetic is Cozmo - 0='no-energy', 1='full energy'
- play_value (float): How in need of play is Cozmo - 0='bored', 1='happy'
- """
- msg = _clad_to_engine_iface.ForceSetNeedsLevels(
- newNeedLevel = [repair_value, energy_value, play_value])
- self.conn.send_msg(msg)
-
- ### Camera Commands ###
-
- def enable_auto_exposure(self):
- '''
- .. deprecated:: 0.12.0
- Use: :meth:`cozmo.camera.Camera.enable_auto_exposure` instead.
- '''
- warnings.warn("The 'robot.enable_auto_exposure' method is deprecated, "
- "use 'robot.camera.enable_auto_exposure' instead.", DeprecationWarning, stacklevel=2)
- self.camera.enable_auto_exposure()
-
- def set_manual_exposure(self, exposure_ms, gain):
- '''
- .. deprecated:: 0.12.0
- Use: :meth:`cozmo.camera.Camera.set_manual_exposure` instead.
- '''
- warnings.warn("The 'robot.set_manual_exposure' method is deprecated, "
- "use 'robot.camera.set_manual_exposure' instead.", DeprecationWarning, stacklevel=2)
- self.camera.set_manual_exposure(exposure_ms, gain)
-
- ### Low-Level Commands ###
-
- def drive_wheel_motors(self, l_wheel_speed, r_wheel_speed,
- l_wheel_acc=None, r_wheel_acc=None):
- '''Tell Cozmo to move his wheels / treads at a given speed.
-
- The wheels will continue to move at that speed until commanded to drive
- at a new speed, or if :meth:`~cozmo.robot.Robot.stop_all_motors` is called.
-
- Args:
- l_wheel_speed (float): Speed of the left tread (in millimeters per second)
- r_wheel_speed (float): Speed of the right tread (in millimeters per second)
- l_wheel_acc (float): Acceleration of left tread (in millimeters per second squared)
- ``None`` value defaults this to the same as l_wheel_speed.
- r_wheel_acc (float): Acceleration of right tread (in millimeters per second squared)
- ``None`` value defaults this to the same as r_wheel_speed.
- '''
- if l_wheel_acc is None:
- l_wheel_acc = l_wheel_speed
- if r_wheel_acc is None:
- r_wheel_acc = r_wheel_speed
-
- msg = _clad_to_engine_iface.DriveWheels(lwheel_speed_mmps=l_wheel_speed,
- rwheel_speed_mmps=r_wheel_speed,
- lwheel_accel_mmps2=l_wheel_acc,
- rwheel_accel_mmps2=r_wheel_acc)
- self.conn.send_msg(msg)
-
- async def drive_wheels(self, l_wheel_speed, r_wheel_speed,
- l_wheel_acc=None, r_wheel_acc=None, duration=None):
- '''Tell Cozmo to move his wheels / treads at a given speed, and optionally stop them after a given duration.
-
- If duration is ``None`` then this is equivalent to the non-async
- :meth:`~cozmo.robot.Robot.drive_wheel_motors` method.
-
- Args:
- l_wheel_speed (float): Speed of the left tread (in millimeters per second).
- r_wheel_speed (float): Speed of the right tread (in millimeters per second).
- l_wheel_acc (float): Acceleration of left tread (in millimeters per second squared).
- ``None`` value defaults this to the same as l_wheel_speed.
- r_wheel_acc (float): Acceleration of right tread (in millimeters per second squared).
- ``None`` value defaults this to the same as r_wheel_speed.
- duration (float): Time for the robot to drive. Will call :meth:`~cozmo.robot.Robot.stop_all_motors`
- after this duration has passed.
- '''
- self.drive_wheel_motors(l_wheel_speed, r_wheel_speed, l_wheel_acc, r_wheel_acc)
- if duration:
- await asyncio.sleep(duration, loop=self._loop)
- self.stop_all_motors()
-
- def stop_all_motors(self):
- '''Tell Cozmo to stop all motors.'''
- msg = _clad_to_engine_iface.StopAllMotors()
- self.conn.send_msg(msg)
-
- def move_head(self, speed):
- '''Tell Cozmo's head motor to move with a certain speed.
-
- Positive speed for up, negative speed for down. Measured in radians per second.
-
- Args:
- speed (float): Motor speed for Cozmo's head, measured in radians per second.
- '''
- msg = _clad_to_engine_iface.MoveHead(speed_rad_per_sec=speed)
- self.conn.send_msg(msg)
-
- def move_lift(self, speed):
- '''Tell Cozmo's lift motor to move with a certain speed.
-
- Positive speed for up, negative speed for down. Measured in radians per second.
-
- Args:
- speed (float): Motor speed for Cozmo's lift, measured in radians per second.
- '''
- msg = _clad_to_engine_iface.MoveLift()
- msg = _clad_to_engine_iface.MoveLift(speed_rad_per_sec=speed)
- self.conn.send_msg(msg)
-
- def say_text(self, text, play_excited_animation=False, use_cozmo_voice=True,
- duration_scalar=1.0, voice_pitch=0.0, in_parallel=False, num_retries=0):
- '''Have Cozmo say text!
-
- Args:
- text (string): The words for Cozmo to say.
- play_excited_animation (bool): Whether to also play an excited
- animation while speaking (moves Cozmo a lot).
- use_cozmo_voice (bool): Whether to use Cozmo's robot voice
- (otherwise, he uses a generic human male voice).
- duration_scalar (float): Adjust the relative duration of the
- generated text to speech audio.
- voice_pitch (float): Adjust the pitch of Cozmo's robot voice [-1.0, 1.0]
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.SayText` action object which can be
- queried to see when it is complete
- '''
-
- action = self.say_text_factory(text=text, play_excited_animation=play_excited_animation,
- use_cozmo_voice=use_cozmo_voice, duration_scalar=duration_scalar,
- voice_pitch=voice_pitch, conn=self.conn,
- robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def set_backpack_lights(self, light1, light2, light3, light4, light5):
- '''Set the lights on Cozmo's backpack.
-
- The light descriptions below are all from Cozmo's perspective.
-
- Note: The left and right lights only contain red LEDs, so e.g. setting
- them to green will look off, and setting them to white will look red
-
- Args:
- light1 (:class:`cozmo.lights.Light`): The left backpack light
- light2 (:class:`cozmo.lights.Light`): The front backpack light
- light3 (:class:`cozmo.lights.Light`): The center backpack light
- light4 (:class:`cozmo.lights.Light`): The rear backpack light
- light5 (:class:`cozmo.lights.Light`): The right backpack light
- '''
- msg = _clad_to_engine_iface.SetBackpackLEDs()
- for i, light in enumerate( (light1, light2, light3, light4, light5) ):
- if light is not None:
- lights._set_light(msg, i, light)
-
- self.conn.send_msg(msg)
-
- def set_center_backpack_lights(self, light):
- '''Set the lights in the center of Cozmo's backpack to the same color.
-
- Forces the lights on the left and right to off (this is useful as those
- lights only support shades of red, so cannot generally be set to the
- same color as the center lights).
-
- Args:
- light (:class:`cozmo.lights.Light`): The lights for Cozmo's backpack.
- '''
- light_arr = [ light ] * 5
- light_arr[0] = lights.off_light
- light_arr[4] = lights.off_light
- self.set_backpack_lights(*light_arr)
-
- def set_all_backpack_lights(self, light):
- '''Set the lights on Cozmo's backpack to the same color.
-
- Args:
- light (:class:`cozmo.lights.Light`): The lights for Cozmo's backpack.
- '''
- light_arr = [ light ] * 5
- self.set_backpack_lights(*light_arr)
-
- def set_backpack_lights_off(self):
- '''Set the lights on Cozmo's backpack to off.'''
- light_arr = [ lights.off_light ] * 5
- self.set_backpack_lights(*light_arr)
-
- def set_head_light(self, enable):
- '''Turn Cozmo's IR headlight on or off.
-
- The headlight is on the front of Cozmo's chassis, between his two
- front wheels, underneath his head. Cozmo's camera is IR sensitive
- so although you cannot see the IR light with the naked eye you will
- see it in Cozmo's camera feed.
-
- Args:
- enable (bool): True turns the light on, False turns it off.
- '''
- msg = _clad_to_engine_iface.SetHeadlight(enable=enable)
- self.conn.send_msg(msg)
-
- def enable_freeplay_cube_lights(self, enable=True):
- """Enable, or disable, the automatic cube light mode used in freeplay.
-
- Enabling the freeplay cube light mode causes the cubes to automatically
- pulse blue when Cozmo can see them - as seen in the Cozmo app during
- freeplay mode. This is disabled by default in SDK mode because it
- overrides any other calls to set the cube light colors.
-
- Args:
- enable (bool): True to enable the freeplay cube light mode,
- False to disable it.
- """
- if enable:
- self._set_cube_light_state(True)
- self._enable_cube_sleep(False, False)
- else:
- self._enable_cube_sleep(True, True)
- self._set_cube_light_state(False)
-
- def set_head_angle(self, angle, accel=10.0, max_speed=10.0, duration=0.0,
- warn_on_clamp=True, in_parallel=False, num_retries=0):
- '''Tell Cozmo's head to turn to a given angle.
-
- Args:
- angle: (:class:`cozmo.util.Angle`): Desired angle for
- Cozmo's head. (:const:`MIN_HEAD_ANGLE` to
- :const:`MAX_HEAD_ANGLE`).
- accel (float): Acceleration of Cozmo's head in radians per second squared.
- max_speed (float): Maximum speed of Cozmo's head in radians per second.
- duration (float): Time for Cozmo's head to turn in seconds. A value
- of zero will make Cozmo try to do it as quickly as possible.
- warn_on_clamp (bool): True to log a warning if the angle had to be
- clamped to the valid range (:const:`MIN_HEAD_ANGLE` to
- :const:`MAX_HEAD_ANGLE`).
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.SetHeadAngle` action object which can be
- queried to see when it is complete
- '''
- action = self.set_head_angle_factory(angle=angle, max_speed=max_speed,
- accel=accel, duration=duration, warn_on_clamp=warn_on_clamp,
- conn=self.conn, robot=self, dispatch_parent=self)
-
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def set_lift_height(self, height, accel=10.0, max_speed=10.0, duration=0.0,
- in_parallel=False, num_retries=0):
- '''Tell Cozmo's lift to move to a given height
-
- Args:
- height (float): desired height for Cozmo's lift 0.0 (bottom) to
- 1.0 (top) (we clamp it to this range internally).
- accel (float): Acceleration of Cozmo's lift in radians per
- second squared.
- max_speed (float): Maximum speed of Cozmo's lift in radians per second.
- duration (float): Time for Cozmo's lift to move in seconds. A value
- of zero will make Cozmo try to do it as quickly as possible.
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.SetLiftHeight` action object which can be
- queried to see when it is complete.
- '''
- action = self.set_lift_height_factory(height=height, max_speed=max_speed,
- accel=accel, duration=duration, conn=self.conn,
- robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
-
- ## Animation Commands ##
-
- def play_audio(self, audio_event):
- '''Sends an audio event to the engine
-
- Most of these come in pairs, with one to start an audio effect, and one to stop
- if desired.
-
- Example:
- :attr:`cozmo.audio.AudioEvents.SfxSharedSuccess` starts a sound
- :attr:`cozmo.audio.AudioEvents.SfxSharedSuccessStop` interrupts that sound in progress
-
- Some events are part of the TinyOrchestra system which have special behavior.
- This system can be intitialized and stopped, and various musical instruments can be
- turned on and off while it is running.
-
- Args:
- audio_event (object): An attribute of the :class:`cozmo.audio.AudioEvents` class
- '''
- audio_event_id = audio_event.id
- game_object_id = _clad_to_engine_anki.AudioMetaData.GameObjectType.CodeLab
-
- msg = _clad_to_engine_anki.AudioEngine.Multiplexer.PostAudioEvent(
- audioEvent=audio_event_id, gameObject=game_object_id)
- self.conn.send_msg(msg)
-
- def play_song(self, song_notes, loop_count=1, in_parallel=False, num_retries=0):
- '''Starts playing song on the robot.
-
- Plays a provided array of SongNotes using a custom animation on the robot.
-
- Args:
- song_notes (object[]): An array of :class:`cozmo.song.SongNote` classes
-
- Returns:
- A :class:`cozmo.anim.Animation` action object which can be queried
- to see when it is complete.
- '''
-
- msg = _clad_to_engine_iface.ReplaceNotesInSong(notes=song_notes)
- self.conn.send_msg(msg)
-
- song_animation_name = 'cozmo_sings_custom'
- action = self.animation_factory(song_animation_name, loop_count,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def play_anim(self, name, loop_count=1, in_parallel=False, num_retries=0,
- ignore_body_track=False, ignore_head_track=False, ignore_lift_track=False):
- '''Starts an animation playing on a robot.
-
- Returns an Animation object as soon as the request to play the animation
- has been sent. Call the wait_for_completed method on the animation
- if you wish to wait for completion (or listen for the
- :class:`cozmo.anim.EvtAnimationCompleted` event).
-
- Warning: Specific animations may be renamed or removed in future updates of the app.
- If you want your program to work more reliably across all versions
- we recommend using :meth:`play_anim_trigger` instead.
-
- Args:
- name (str): The name of the animation to play.
- loop_count (int): Number of times to play the animation.
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- ignore_body_track (bool): True to ignore the animation track for
- Cozmo's body (i.e. the wheels / treads).
- ignore_head_track (bool): True to ignore the animation track for
- Cozmo's head.
- ignore_lift_track (bool): True to ignore the animation track for
- Cozmo's lift.
- Returns:
- A :class:`cozmo.anim.Animation` action object which can be queried
- to see when it is complete.
- Raises:
- :class:`ValueError` if supplied an invalid animation name.
- '''
- if name not in self.conn.anim_names:
- raise ValueError('Unknown animation name "%s"' % name)
- action = self.animation_factory(name, loop_count,
- ignore_body_track, ignore_head_track, ignore_lift_track,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def play_anim_trigger(self, trigger, loop_count=1, in_parallel=False,
- num_retries=0, use_lift_safe=False, ignore_body_track=False,
- ignore_head_track=False, ignore_lift_track=False):
- """Starts an animation trigger playing on a robot.
-
- As noted in the Triggers class, playing a trigger requests that an
- animation of a certain class starts playing, rather than an exact
- animation name as influenced by the robot's mood, and other factors.
-
- Args:
- trigger (object): An attribute of the :class:`cozmo.anim.Triggers` class
- loop_count (int): Number of times to play the animation
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- use_lift_safe (bool): True to automatically ignore the lift track
- if Cozmo is currently carrying an object.
- ignore_body_track (bool): True to ignore the animation track for
- Cozmo's body (i.e. the wheels / treads).
- ignore_head_track (bool): True to ignore the animation track for
- Cozmo's head.
- ignore_lift_track (bool): True to ignore the animation track for
- Cozmo's lift.
- Returns:
- A :class:`cozmo.anim.AnimationTrigger` action object which can be
- queried to see when it is complete
- Raises:
- :class:`ValueError` if supplied an invalid animation trigger.
- """
- if not isinstance(trigger, anim._AnimTrigger):
- raise TypeError("Invalid trigger supplied")
-
- action = self.animation_trigger_factory(trigger, loop_count, use_lift_safe,
- ignore_body_track, ignore_head_track, ignore_lift_track,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def set_idle_animation(self, anim_trigger):
- '''Set the Idle Animation on Cozmo
-
- Idle animations keep Cozmo alive inbetween the times other animations play.
- They behave the same as regular animations except that they
- loop forever until another animation is started.
-
- Args:
- anim_trigger (:class:`cozmo.anim.Triggers`): The animation trigger to set
- Raises:
- :class:`ValueError` if supplied an invalid animation trigger.
- '''
- if not isinstance(anim_trigger, anim._AnimTrigger):
- raise TypeError("Invalid anim_trigger supplied")
-
- msg = _clad_to_engine_iface.PushIdleAnimation(animTrigger=anim_trigger.id,
- lockName="sdk")
- self.conn.send_msg(msg)
- self._idle_stack_depth += 1
-
-
- def clear_idle_animation(self):
- '''Clears any Idle Animation currently playing on Cozmo'''
- msg = _clad_to_engine_iface.RemoveIdleAnimation(lockName="sdk")
-
- while(self._idle_stack_depth > 0):
- self.conn.send_msg(msg)
- self._idle_stack_depth -= 1
-
- # Cozmo's Face animation commands
-
- def display_oled_face_image(self, screen_data, duration_ms,
- in_parallel=True):
- ''' Display a bitmap image on Cozmo's OLED face screen.
-
- Args:
- screen_data (:class:`bytes`): a sequence of pixels (8 pixels per
- byte) (from e.g.
- :func:`cozmo.oled_face.convert_pixels_to_screen_data`).
- duration_ms (float): time to keep displaying this image on Cozmo's
- face (clamped to 30 seconds in engine).
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- Returns:
- A :class:`cozmo.robot.DisplayOledFaceImage` action object which
- can be queried to see when it is complete.
- Raises:
- :class:`cozmo.exceptions.RobotBusy` if another action is already
- running and in_parallel==False
- '''
-
- # We never want 2 face image actions active at once, so clear current
- # face image action (if one is running)
- if ((self._current_face_image_action is not None) and
- self._current_face_image_action.is_running):
- self._current_face_image_action.abort()
-
- action = self.display_oled_face_image_factory(screen_data=screen_data,
- duration_ms=duration_ms,
- conn=self.conn, robot=self,
- dispatch_parent=self)
- self._current_face_image_action = action
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=0)
- return action
-
-
- ## Behavior Commands ##
-
- def start_behavior(self, behavior_type):
- '''Starts executing a behavior.
-
- Call the :meth:`~cozmo.behavior.Behavior.stop` method on the behavior
- object at some point in the future to terminate execution.
-
- Args:
- behavior_type (:class:`cozmo.behavior._BehaviorType`): An attribute of
- :class:`cozmo.behavior.BehaviorTypes`.
- Returns:
- :class:`cozmo.behavior.Behavior`
- Raises:
- :class:`TypeError` if an invalid behavior type is supplied.
- '''
- if not isinstance(behavior_type, behavior._BehaviorType):
- raise TypeError('Invalid behavior supplied')
-
- if self._current_behavior is not None:
- self._current_behavior._set_stopped()
-
- new_behavior = self.behavior_factory(self, behavior_type,
- is_active=True, dispatch_parent=self)
- msg = _clad_to_engine_iface.ExecuteBehaviorByExecutableType(
- behaviorType=behavior_type.id)
- self.conn.send_msg(msg)
- self._current_behavior = new_behavior
- return new_behavior
-
- async def run_timed_behavior(self, behavior_type, active_time):
- '''Executes a behavior for a set number of seconds.
-
- This call blocks and stops the behavior after active_time seconds.
-
- Args:
- behavior_type (:class:`cozmo.behavior._BehaviorType`): An attribute of
- :class:`cozmo.behavior.BehaviorTypes`.
- active_time (float): specifies the maximum time to execute in seconds
- Raises:
- :class:`TypeError` if an invalid behavior type is supplied.
- '''
- b = self.start_behavior(behavior_type)
- try:
- await b.wait_for_completed(timeout=active_time)
- except asyncio.TimeoutError:
- # It didn't complete within the time, stop it
- b.stop()
-
- def start_freeplay_behaviors(self):
- '''Start running freeplay behaviors on Cozmo
-
- Puts Cozmo into a freeplay mode where he autonomously drives around
- and does stuff based on his mood and environment.
-
- You shouldn't attempt to drive Cozmo during this, as it will clash
- with whatever the current behavior is attempting to do.
- '''
- msg = _clad_to_engine_iface.ActivateHighLevelActivity(
- _clad_to_engine_cozmo.HighLevelActivity.Freeplay)
- self.conn.send_msg(msg)
-
- self._is_behavior_running = True # The chooser will run them automatically
- self._is_freeplay_mode_active = True
-
- def stop_freeplay_behaviors(self):
- '''Stop running freeplay behaviors on Cozmo
-
- Forces Cozmo out of Freeplay mode and stops any currently running
- behaviors and actions.
- '''
-
- msg = _clad_to_engine_iface.ActivateHighLevelActivity(
- _clad_to_engine_cozmo.HighLevelActivity.Selection)
- self.conn.send_msg(msg)
-
- self._is_freeplay_mode_active = False
- self._set_none_behavior()
- self.abort_all_actions()
-
- ## Object Commands ##
-
- def pickup_object(self, obj, use_pre_dock_pose=True, in_parallel=False,
- num_retries=0):
- '''Instruct the robot to pick up the supplied object.
-
- Args:
- obj (:class:`cozmo.objects.ObservableObject`): The target object to
- pick up where ``obj.pickupable`` is True.
- use_pre_dock_pose (bool): whether or not to try to immediately pick
- up an object or first position the robot next to the object.
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.PickupObject` action object which can be
- queried to see when it is complete.
- Raises:
- :class:`cozmo.exceptions.RobotBusy` if another action is already
- running and in_parallel==False
- :class:`cozmo.exceptions.NotPickupable` if object type can't be picked up.
- '''
- if not obj.pickupable:
- raise exceptions.NotPickupable('Cannot pickup this type of object')
-
- # TODO: Check with the World to see if Cozmo is already holding an object.
- logger.info("Sending pickup object request for object=%s", obj)
- action = self.pickup_object_factory(obj=obj, use_pre_dock_pose=use_pre_dock_pose,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def place_on_object(self, obj, use_pre_dock_pose=True, in_parallel=False,
- num_retries=0):
- '''Asks Cozmo to place the currently held object onto a target object.
-
- Args:
- obj (:class:`cozmo.objects.ObservableObject`): The target object to
- place current held object on, where obj.place_objects_on_this
- is True.
- use_pre_dock_pose (bool): Whether or not to try to immediately pick
- up an object or first position the robot next to the object.
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.PlaceOnObject` action object which can be
- queried to see when it is complete.
- Raises:
- :class:`cozmo.exceptions.RobotBusy` if another action is already
- running and in_parallel==False
- :class:`cozmo.exceptions.CannotPlaceObjectsOnThis` if the object cannot have objects
- placed on it.
- '''
- if not obj.place_objects_on_this:
- raise exceptions.CannotPlaceObjectsOnThis('Cannot place objects on this type of object')
-
- # TODO: Check with the World to see if Cozmo is already holding an object.
- logger.info("Sending place on object request for target object=%s", obj)
- action = self.place_on_object_factory(obj=obj, use_pre_dock_pose=use_pre_dock_pose,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def place_object_on_ground_here(self, obj, in_parallel=False, num_retries=0):
- '''Ask Cozmo to place the object he is carrying on the ground at the current location.
-
- Args:
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.PlaceObjectOnGroundHere` action object which
- can be queried to see when it is complete.
- Raises:
- :class:`cozmo.exceptions.RobotBusy` if another action is already
- running and in_parallel==False
- '''
- # TODO: Check whether Cozmo is known to be holding the object in question
- logger.info("Sending place down here request for object=%s", obj)
- action = self.place_object_on_ground_here_factory(obj=obj,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
-
- ## Interact with seen Face Commands ##
-
- def turn_towards_face(self, face, in_parallel=False, num_retries=0):
- '''Tells Cozmo to turn towards this face.
-
- Args:
- face: (:class:`cozmo.faces.Face`): The face Cozmo will turn towards.
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.TurnTowardsFace` action object which can be
- queried to see when it is complete
- '''
- action = self.turn_towards_face_factory(face=face,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
-
- ## Robot Driving Commands ##
-
- def go_to_pose(self, pose, relative_to_robot=False, in_parallel=False,
- num_retries=0):
- '''Tells Cozmo to drive to the specified pose and orientation.
-
- If relative_to_robot is set to True, the given pose will assume the
- robot's pose as its origin.
-
- Since the robot understands position by monitoring its tread movement,
- it does not understand movement in the z axis. This means that the only
- applicable elements of pose in this situation are position.x position.y
- and rotation.angle_z.
-
- Args:
- pose: (:class:`cozmo.util.Pose`): The destination pose.
- relative_to_robot (bool): Whether the given pose is relative to
- the robot's pose.
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.GoToPose` action object which can be queried
- to see when it is complete.
- '''
- if relative_to_robot:
- pose = self.pose.define_pose_relative_this(pose)
- action = self.go_to_pose_factory(pose=pose,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def go_to_object(self, target_object, distance_from_object,
- in_parallel=False, num_retries=0):
- '''Tells Cozmo to drive to the specified object.
-
- Args:
- target_object (:class:`cozmo.objects.ObservableObject`): The destination object.
- CustomObject instances are not supported.
- distance_from_object (:class:`cozmo.util.Distance`): The distance from the
- object to stop. This is the distance between the origins. For instance,
- the distance from the robot's origin (between Cozmo's two front wheels)
- to the cube's origin (at the center of the cube) is ~40mm.
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.GoToObject` action object which can be queried
- to see when it is complete.
- '''
- if not isinstance(target_object, objects.ObservableObject):
- raise TypeError("Target must be an observable object")
-
- if isinstance(target_object, objects.CustomObject):
- raise TypeError("CustomObject instances not supported by go_to_object")
-
- action = self.go_to_object_factory(object_id=target_object.object_id,
- distance_from_object=distance_from_object,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def dock_with_cube(self, target_object, approach_angle=None,
- alignment_type=robot_alignment.RobotAlignmentTypes.LiftPlate,
- distance_from_marker=None,
- in_parallel=False, num_retries=0):
- '''Tells Cozmo to dock with a specified cube object.
-
- Args:
- target_object (:class:`cozmo.objects.LightCube`): The cube to dock with.
- approach_angle (:class:`cozmo.util.Angle`): The angle to approach the
- cube from. For example, 180 degrees will cause cozmo to drive
- past the cube and approach it from behind.
- alignment_type (:class:`cozmo.robot_alignment.RobotAlignmentTypes`):
- which part of the robot to line up with the front of the object.
- distance_from_marker (:class:`cozmo.util.Distance`): distance from
- the cube marker to stop when using Custom alignment
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.DockWithCube` action object which can be queried
- to see when it is complete.
- '''
- if not isinstance(target_object, objects.LightCube):
- raise TypeError("Target must be a light cube")
-
- action = self.dock_with_cube_factory(obj=target_object, approach_angle=approach_angle,
- alignment_type=alignment_type, distance_from_marker=distance_from_marker,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def roll_cube(self, target_object, approach_angle=None, check_for_object_on_top=False,
- in_parallel=False, num_retries=0):
- '''Tells Cozmo to roll a specified cube object.
-
- Args:
- target_object (:class:`cozmo.objects.LightCube`): The cube to roll.
- approach_angle (:class:`cozmo.util.Angle`): The angle to approach the
- cube from. For example, 180 degrees will cause cozmo to drive
- past the cube and approach it from behind.
- check_for_object_on_top (bool): If there is a cube on top of the
- specified cube, and check_for_object_on_top is True, then Cozmo
- will ignore the action.
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.RollCube` action object which can be queried
- to see when it is complete.
- '''
- if not isinstance(target_object, objects.LightCube):
- raise TypeError("Target must be a light cube")
-
- action = self.roll_cube_factory(obj=target_object, approach_angle=approach_angle,
- check_for_object_on_top=check_for_object_on_top,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def pop_a_wheelie(self, target_object, approach_angle=None,
- in_parallel=False, num_retries=0):
- '''Tells Cozmo to "pop a wheelie" using a light cube.
-
- Args:
- target_object (:class:`cozmo.objects.LightCube`): The cube to push
- down on with cozmo's lift, to start the wheelie.
- approach_angle (:class:`cozmo.util.Angle`): The angle to approach the
- cube from. For example, 180 degrees will cause cozmo to drive
- past the cube and approach it from behind.
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.PopAWheelie` action object which can be queried
- to see when it is complete.
- '''
- if not isinstance(target_object, objects.LightCube):
- raise TypeError("Target must be a light cube")
-
- action = self.pop_a_wheelie_factory(obj=target_object,
- approach_angle=approach_angle,
- conn=self.conn,
- robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def turn_in_place(self, angle, in_parallel=False, num_retries=0, speed=None,
- accel=None, angle_tolerance=None, is_absolute=False):
- '''Turn the robot around its current position.
-
- Args:
- angle (:class:`cozmo.util.Angle`): The angle to turn. Positive
- values turn to the left, negative values to the right.
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- speed (:class:`cozmo.util.Angle`): Angular turn speed (per second).
- accel (:class:`cozmo.util.Angle`): Acceleration of angular turn
- (per second squared).
- angle_tolerance (:class:`cozmo.util.Angle`): angular tolerance
- to consider the action complete (this is clamped to a minimum
- of 2 degrees internally).
- is_absolute (bool): True to turn to a specific angle, False to
- turn relative to the current pose.
- Returns:
- A :class:`cozmo.robot.TurnInPlace` action object which can be
- queried to see when it is complete.
- '''
- action = self.turn_in_place_factory(angle=angle, speed=speed,
- accel=accel, angle_tolerance=angle_tolerance, is_absolute=is_absolute,
- conn=self.conn, robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- def drive_off_charger_contacts(self, in_parallel=False, num_retries=0):
- '''Tells Cozmo to drive forward slightly to get off the charger contacts.
-
- All motor movement is disabled while Cozmo is on the charger to
- prevent hardware damage. This command is the one exception and provides
- a way to drive forward a little to disconnect from the charger contacts
- and thereby re-enable all other commands.
-
- Args:
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.DriveOffChargerContacts` action object which
- can be queried to see when it is complete.
- '''
- action = self.drive_off_charger_contacts_factory(conn=self.conn,
- robot=self, dispatch_parent=self)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- async def backup_onto_charger(self, max_drive_time=3):
- '''Attempts to reverse robot onto its charger.
-
- This method assumes the charger is directly behind the robot and
- will keep driving straight back until charger is in contact, or until
- a timeout is reached.
-
- Args:
- max_drive_time (float): The maximum amount of time in seconds
- to reverse the robot without detecting the charger.
- '''
-
- await self.drive_wheels(-30, -30)
- timeout = util.Timeout(timeout=max_drive_time)
- while not (timeout.is_timed_out or self.is_on_charger) :
- await asyncio.sleep(0.1, loop=self.loop)
-
- self.stop_all_motors()
-
- def perform_off_charger(self):
- '''Returns a context manager to move the robot off of and back onto the charger.
-
- If the robot is on the charger, it will move a short distance off the contacts,
- perform the code wrapped by the context and then move the robot back onto the
- charger after the wrapped code completes.
-
- Synchronous example::
-
- with robot.perform_off_charger():
- action = robot.say_text("Hello")
- action.wait_for_completed()
-
- Asynchronous example::
-
- async with robot.perform_off_charger():
- action = robot.say_text("Hello")
- await action.wait_for_completed()
- '''
- return self.perform_off_charger_factory(self)
-
- def drive_straight(self, distance, speed, should_play_anim=True,
- in_parallel=False, num_retries=0):
- '''Tells Cozmo to drive in a straight line
-
- Cozmo will drive for the specified distance (forwards or backwards)
-
- Args:
- distance (:class:`cozmo.util.Distance`): The distance to drive
- (>0 for forwards, <0 for backwards)
- speed (:class:`cozmo.util.Speed`): The speed to drive at
- (should always be >0, the abs(speed) is used internally)
- should_play_anim (bool): Whether to play idle animations
- whilst driving (tilt head, hum, animated eyes, etc.)
- in_parallel (bool): True to run this action in parallel with
- previous actions, False to require that all previous actions
- be already complete.
- num_retries (int): Number of times to retry the action if the
- previous attempt(s) failed.
- Returns:
- A :class:`cozmo.robot.DriveStraight` action object which
- can be queried to see when it is complete.
- '''
- action = self.drive_straight_factory(conn=self.conn,
- robot=self,
- dispatch_parent=self,
- distance=distance,
- speed=speed,
- should_play_anim=should_play_anim)
- self._action_dispatcher._send_single_action(action,
- in_parallel=in_parallel,
- num_retries=num_retries)
- return action
-
- async def wait_for_all_actions_completed(self):
- '''Waits until all SDK-initiated actions are complete.'''
- await self._action_dispatcher.wait_for_all_actions_completed()
-
-
- _UnexpectedMovementSide = collections.namedtuple('_UnexpectedMovementSide', ['name', 'id'])
-
- class UnexpectedMovementSide(CladEnumWrapper):
- '''Defines the side of collision that caused unexpected movement.
-
- This will always be UNKNOWN while reaction triggers are disabled.
- Call :meth:`cozmo.robot.Robot.enable_all_reaction_triggers` to enable reaction triggers.
- '''
- _clad_enum = _clad_to_engine_cozmo.UnexpectedMovementSide
- _entry_type = _UnexpectedMovementSide
-
- #: Unable to tell what side obstructed movement.
- #: Usually caused by reaction triggers being disabled.
- Unknown = _entry_type("Unknown", _clad_enum.UNKNOWN)
-
- #: Obstruction detected in front of the robot.
- Front = _entry_type("Front", _clad_enum.FRONT)
-
- #: Obstruction detected behind the robot.
- Back = _entry_type("Back", _clad_enum.BACK)
-
- #: Obstruction detected to the left of the robot
- Left = _entry_type("Left", _clad_enum.LEFT)
-
- #: Obstruction detected to the right of the robot
- Right = _entry_type("Right", _clad_enum.RIGHT)
-
- UnexpectedMovementSide._init_class()
-
-
- _UnexpectedMovementType = collections.namedtuple('_UnexpectedMovementType', ['name', 'id'])
-
- class UnexpectedMovementType(CladEnumWrapper):
- '''Defines the type of unexpected movement.'''
- _clad_enum = _clad_to_engine_cozmo.UnexpectedMovementType
- _entry_type = _UnexpectedMovementType
-
- #: Tried to turn, but couldn't.
- TurnedButStopped = _entry_type("TurnedButStopped", _clad_enum.TURNED_BUT_STOPPED)
-
- # Turned in the expected direction, but turned further than expected.
- # Currently unused.
- _TurnedInSameDirection = _entry_type("TurnedInSameDirection", _clad_enum.TURNED_IN_SAME_DIRECTION)
-
- #: Expected to turn in one direction, but turned the other way.
- #: Also happens when rotation is unexpected.
- TurnedInOppositeDirection = _entry_type("TurnedInOppositeDirection", _clad_enum.TURNED_IN_OPPOSITE_DIRECTION)
-
- UnexpectedMovementType._init_class()
|