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

robot.py 102KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335
  1. # Copyright (c) 2016-2017 Anki, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License in the file LICENSE.txt or at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. '''Classes and functions relating to an individual Cozmo robot.
  15. The :meth:`cozmo.conn.CozmoConnection.wait_for_robot` method returns an
  16. instance of :class:`Robot` which controls a single Cozmo robot.
  17. The :class:`Robot` class has methods and properties to determine its current
  18. state, control its low-level motors, play animations and start behaviors as
  19. well as performing high-level actions such as detecting faces and picking up
  20. objects.
  21. Each :class:`Robot` has a :attr:`Robot.world` attribute which represents an
  22. instance of a :class:`cozmo.world.World`. This tracks the state of the world
  23. that Cozmo knows about: The objects and faces it's currently observing,
  24. the camera images it's receiving, etc. You can monitor the world instance for
  25. various events that occur, or monitor individual objects directly: The
  26. world instance receives all events that the robot triggers, and nearly all SDK
  27. objects inherit from :class:`cozmo.event.Dispatcher` and therefore inherit
  28. methods such as :meth:`~cozmo.event.Dispatcher.wait_for` and
  29. :meth:`~cozmo.event.Dispatcher.add_event_handler`.
  30. '''
  31. # __all__ should order by constants, event classes, other classes, functions.
  32. __all__ = ['MIN_HEAD_ANGLE', 'MAX_HEAD_ANGLE',
  33. 'MIN_LIFT_HEIGHT', 'MIN_LIFT_HEIGHT_MM', 'MAX_LIFT_HEIGHT', 'MAX_LIFT_HEIGHT_MM',
  34. 'MIN_LIFT_ANGLE', 'MAX_LIFT_ANGLE',
  35. # Event classes
  36. 'EvtRobotReady', 'EvtRobotStateUpdated', 'EvtUnexpectedMovement',
  37. # Helper classes
  38. 'LiftPosition', 'UnexpectedMovementSide', 'UnexpectedMovementType',
  39. # Robot Action classes
  40. 'DisplayOledFaceImage', 'DockWithCube', 'DriveOffChargerContacts', 'DriveStraight',
  41. 'GoToObject', 'GoToPose', 'PerformOffChargerContext', 'PickupObject',
  42. 'PlaceObjectOnGroundHere', 'PlaceOnObject', 'PopAWheelie', 'RollCube', 'SayText',
  43. 'SetHeadAngle', 'SetLiftHeight', 'TurnInPlace', 'TurnTowardsFace',
  44. # Robot
  45. 'Robot']
  46. import asyncio
  47. import collections
  48. import math
  49. import warnings
  50. from . import logger, logger_protocol
  51. from . import action
  52. from . import anim
  53. from . import audio
  54. from . import song
  55. from . import behavior
  56. from . import camera
  57. from . import conn
  58. from . import event
  59. from . import exceptions
  60. from . import lights
  61. from . import objects
  62. from . import util
  63. from . import world
  64. from . import robot_alignment
  65. from ._clad import _clad_to_engine_iface, _clad_to_engine_cozmo, _clad_to_engine_anki, _clad_to_game_cozmo, CladEnumWrapper
  66. #### Events
  67. class EvtRobotReady(event.Event):
  68. '''Generated when the robot has been initialized and is ready for commands.'''
  69. robot = "Robot object representing the robot to command"
  70. class EvtRobotStateUpdated(event.Event):
  71. '''Dispatched whenever the robot's state is updated (multiple times per second).'''
  72. robot = "Robot object representing the robot to command"
  73. class EvtUnexpectedMovement(event.Event):
  74. '''Triggered whenever the robot does not move as expected (typically rotation).'''
  75. robot = "Robot object representing the robot to command"
  76. timestamp = "Robot timestamp for when the unexpected movement occurred"
  77. movement_type = "An UnexpectedMovementType Object representing the type of unexpected movement"
  78. movement_side = "An UnexpectedMovementSide Object representing the side that is obstructing movement"
  79. #### Constants
  80. #: The minimum angle the robot's head can be set to
  81. MIN_HEAD_ANGLE = util.degrees(-25)
  82. #: The maximum angle the robot's head can be set to
  83. MAX_HEAD_ANGLE = util.degrees(44.5)
  84. # The lowest height-above-ground that lift can be moved to in millimeters.
  85. MIN_LIFT_HEIGHT_MM = 32.0
  86. #: The lowest height-above-ground that lift can be moved to
  87. MIN_LIFT_HEIGHT = util.distance_mm(MIN_LIFT_HEIGHT_MM)
  88. # The largest height-above-ground that lift can be moved to in millimeters.
  89. MAX_LIFT_HEIGHT_MM = 92.0
  90. #: The largest height-above-ground that lift can be moved to
  91. MAX_LIFT_HEIGHT = util.distance_mm(MAX_LIFT_HEIGHT_MM)
  92. #: The length of Cozmo's lift arm
  93. LIFT_ARM_LENGTH = util.distance_mm(66.0)
  94. #: The height above ground of Cozmo's lift arm's pivot
  95. LIFT_PIVOT_HEIGHT = util.distance_mm(45.0)
  96. #: The minimum angle the robot's lift can be set to
  97. MIN_LIFT_ANGLE = util.radians(math.asin((MIN_LIFT_HEIGHT_MM - LIFT_PIVOT_HEIGHT.distance_mm) / LIFT_ARM_LENGTH.distance_mm))
  98. #: The maximum angle the robot's lift can be set to
  99. MAX_LIFT_ANGLE = util.radians(math.asin((MAX_LIFT_HEIGHT_MM - LIFT_PIVOT_HEIGHT.distance_mm) / LIFT_ARM_LENGTH.distance_mm))
  100. class LiftPosition:
  101. '''Represents the position of Cozmo's lift.
  102. The class allows the position to be referred to as either absolute height
  103. above the ground, as a ratio from 0.0 to 1.0, or as the angle of the lift
  104. arm relative to the ground.
  105. Args:
  106. height (:class:`cozmo.util.Distance`): The height of the lift above the ground.
  107. ratio (float): The ratio from 0.0 to 1.0 that the lift is raised from the ground.
  108. angle (:class:`cozmo.util.Angle`): The angle of the lift arm relative to the ground.
  109. '''
  110. __slots__ = ('_height')
  111. def __init__(self, height=None, ratio=None, angle=None):
  112. def _count_arg(arg):
  113. # return 1 if argument is set (not None), 0 otherwise
  114. return 0 if (arg is None) else 1
  115. num_provided_args = _count_arg(height) + _count_arg(ratio) + _count_arg(angle)
  116. if num_provided_args != 1:
  117. raise ValueError("Expected one, and only one, of the distance, ratio or angle keyword arguments")
  118. if height is not None:
  119. if not isinstance(height, util.Distance):
  120. raise TypeError("Unsupported type for distance - expected util.Distance")
  121. self._height = height
  122. elif ratio is not None:
  123. height_mm = MIN_LIFT_HEIGHT_MM + (ratio * (MAX_LIFT_HEIGHT_MM - MIN_LIFT_HEIGHT_MM))
  124. self._height = util.distance_mm(height_mm)
  125. elif angle is not None:
  126. if not isinstance(angle, util.Angle):
  127. raise TypeError("Unsupported type for angle - expected util.Angle")
  128. height_mm = (math.sin(angle.radians) * LIFT_ARM_LENGTH.distance_mm) + LIFT_PIVOT_HEIGHT.distance_mm
  129. self._height = util.distance_mm(height_mm)
  130. def __repr__(self):
  131. return "<%s height=%s ratio=%s angle=%s>" % (self.__class__.__name__, self._height, self.ratio, self.angle)
  132. @property
  133. def height(self):
  134. ''':class:`cozmo.util.Distance`: The height above the ground.'''
  135. return self._height
  136. @property
  137. def ratio(self):
  138. '''float: The ratio from 0 to 1 that the lift is raised, 0 at the bottom, 1 at the top.'''
  139. ratio = ((self._height.distance_mm - MIN_LIFT_HEIGHT_MM) /
  140. (MAX_LIFT_HEIGHT_MM - MIN_LIFT_HEIGHT_MM))
  141. return ratio
  142. @property
  143. def angle(self):
  144. ''':class:`cozmo.util.Angle`: The angle of the lift arm relative to the ground.'''
  145. sin_angle = (self._height.distance_mm - LIFT_PIVOT_HEIGHT.distance_mm) / LIFT_ARM_LENGTH.distance_mm
  146. angle_radians = math.asin(sin_angle)
  147. return util.radians(angle_radians)
  148. #### Actions
  149. class GoToPose(action.Action):
  150. '''Represents the go to pose action in progress.
  151. Returned by :meth:`~cozmo.robot.Robot.go_to_pose`
  152. '''
  153. def __init__(self, pose, **kw):
  154. super().__init__(**kw)
  155. self.pose = pose
  156. def _repr_values(self):
  157. return "pose=%s" % (self.pose)
  158. def _encode(self):
  159. return _clad_to_engine_iface.GotoPose(x_mm=self.pose.position.x,
  160. y_mm=self.pose.position.y,
  161. rad=self.pose.rotation.angle_z.radians)
  162. class GoToObject(action.Action):
  163. '''Represents the go to object action in progress.
  164. Returned by :meth:`~cozmo.robot.Robot.go_to_object`
  165. '''
  166. def __init__(self, object_id, distance_from_object, **kw):
  167. super().__init__(**kw)
  168. self.object_id = object_id
  169. self.distance_from_object = distance_from_object
  170. def _repr_values(self):
  171. return "object_id=%s, distance_from_object=%s" % (self.object_id, self.distance_from_object)
  172. def _encode(self):
  173. return _clad_to_engine_iface.GotoObject(objectID=self.object_id,
  174. distanceFromObjectOrigin_mm=self.distance_from_object.distance_mm,
  175. useManualSpeed=False,
  176. usePreDockPose=False)
  177. class DockWithCube(action.Action):
  178. '''Represents the dock with cube action in progress.
  179. Returned by :meth:`~cozmo.robot.Robot.dock_with_cube`
  180. '''
  181. def __init__(self, obj, approach_angle, alignment_type, distance_from_marker, **kw):
  182. super().__init__(**kw)
  183. #: The object (e.g. an instance of :class:`cozmo.objects.LightCube`) that is being put down
  184. self.obj = obj
  185. self.alignment_type = alignment_type
  186. if approach_angle is None:
  187. self.use_approach_angle = False
  188. self.approach_angle = util.degrees(0)
  189. else:
  190. self.use_approach_angle = True
  191. self.approach_angle = approach_angle
  192. if distance_from_marker is None:
  193. self.distance_from_marker = util.distance_mm(0)
  194. else:
  195. self.distance_from_marker = distance_from_marker
  196. def _repr_values(self):
  197. return "object=%s" % (self.obj)
  198. def _encode(self):
  199. return _clad_to_engine_iface.AlignWithObject(objectID=self.obj.object_id,
  200. distanceFromMarker_mm=self.distance_from_marker.distance_mm,
  201. approachAngle_rad=self.approach_angle.radians,
  202. alignmentType=self.alignment_type.id,
  203. useApproachAngle=self.use_approach_angle,
  204. usePreDockPose=self.use_approach_angle,
  205. useManualSpeed=False)
  206. class RollCube(action.Action):
  207. '''Represents the roll cube action in progress.
  208. Returned by :meth:`~cozmo.robot.Robot.roll_cube`
  209. '''
  210. def __init__(self, obj, approach_angle, check_for_object_on_top, **kw):
  211. super().__init__(**kw)
  212. #: The object (e.g. an instance of :class:`cozmo.objects.LightCube`) that is being put down
  213. self.obj = obj
  214. #: bool: whether to check if there is an object on top
  215. self.check_for_object_on_top = check_for_object_on_top
  216. if approach_angle is None:
  217. self.use_approach_angle = False
  218. self.approach_angle = util.degrees(0)
  219. else:
  220. self.use_approach_angle = True
  221. self.approach_angle = approach_angle
  222. def _repr_values(self):
  223. return "object=%s, check_for_object_on_top=%s, approach_angle=%s" % (self.obj, self.check_for_object_on_top, self.approach_angle)
  224. def _encode(self):
  225. return _clad_to_engine_iface.RollObject(objectID=self.obj.object_id,
  226. approachAngle_rad=self.approach_angle.radians,
  227. useApproachAngle=self.use_approach_angle,
  228. usePreDockPose=self.use_approach_angle,
  229. useManualSpeed=False,
  230. checkForObjectOnTop=self.check_for_object_on_top)
  231. class DriveOffChargerContacts(action.Action):
  232. '''Represents the drive off charger contacts action in progress.
  233. Returned by :meth:`~cozmo.robot.Robot.drive_off_charger_contacts`
  234. '''
  235. def __init__(self, **kw):
  236. super().__init__(**kw)
  237. def _repr_values(self):
  238. return ""
  239. def _encode(self):
  240. return _clad_to_engine_iface.DriveOffChargerContacts()
  241. class DriveStraight(action.Action):
  242. '''Represents the "drive straight" action in progress.
  243. Returned by :meth:`~cozmo.robot.Robot.drive_straight`
  244. '''
  245. def __init__(self, distance, speed, should_play_anim, **kw):
  246. super().__init__(**kw)
  247. #: :class:`cozmo.util.Distance`: The distance to drive
  248. self.distance = distance
  249. #: :class:`cozmo.util.Speed`: The speed to drive at
  250. self.speed = speed
  251. #: bool: Whether to play an animation whilst driving
  252. self.should_play_anim = should_play_anim
  253. def _repr_values(self):
  254. return "distance=%s speed=%s should_play_anim=%s" % (self.distance, self.speed, self.should_play_anim)
  255. def _encode(self):
  256. return _clad_to_engine_iface.DriveStraight(speed_mmps=self.speed.speed_mmps,
  257. dist_mm=self.distance.distance_mm,
  258. shouldPlayAnimation=self.should_play_anim)
  259. class DisplayOledFaceImage(action.Action):
  260. '''Represents the "display oled face image" action in progress.
  261. Returned by :meth:`~cozmo.robot.Robot.display_oled_face_image`
  262. '''
  263. # Face images are sent so frequently, with the previous face image always
  264. # aborted, that logging each event would spam the log.
  265. _enable_abort_logging = False
  266. def __init__(self, screen_data, duration_ms, **kw):
  267. super().__init__(**kw)
  268. #: :class:`bytes`: a sequence of pixels (8 pixels per byte)
  269. self.screen_data = screen_data
  270. #: float: time to keep displaying this image on Cozmo's face
  271. self.duration_ms = duration_ms
  272. def _repr_values(self):
  273. return "screen_data=%s Bytes duration_ms=%s" %\
  274. (len(self.screen_data), self.duration_ms)
  275. def _encode(self):
  276. return _clad_to_engine_iface.DisplayFaceImage(faceData=self.screen_data,
  277. duration_ms=self.duration_ms)
  278. class PickupObject(action.Action):
  279. '''Represents the pickup object action in progress.
  280. Returned by :meth:`~cozmo.robot.Robot.pickup_object`
  281. '''
  282. def __init__(self, obj, use_pre_dock_pose=True, **kw):
  283. super().__init__(**kw)
  284. #: The object (e.g. an instance of :class:`cozmo.objects.LightCube`) that was picked up
  285. self.obj = obj
  286. #: A bool that is true when Cozmo needs to go to a pose before attempting to navigate to the object
  287. self.use_pre_dock_pose = use_pre_dock_pose
  288. def _repr_values(self):
  289. return "object=%s" % (self.obj,)
  290. def _encode(self):
  291. return _clad_to_engine_iface.PickupObject(objectID=self.obj.object_id,
  292. usePreDockPose=self.use_pre_dock_pose)
  293. class PlaceOnObject(action.Action):
  294. '''Tracks the state of the "place on object" action.
  295. return by :meth:`~cozmo.robot.Robot.place_on_object`
  296. '''
  297. def __init__(self, obj, use_pre_dock_pose=True, **kw):
  298. super().__init__(**kw)
  299. #: The object (e.g. an instance of :class:`cozmo.objects.LightCube`) that the held object will be placed on
  300. self.obj = obj
  301. #: A bool that is true when Cozmo needs to go to a pose before attempting to navigate to the object
  302. self.use_pre_dock_pose = use_pre_dock_pose
  303. def _repr_values(self):
  304. return "object=%s use_pre_dock_pose=%s" % (self.obj, self.use_pre_dock_pose)
  305. def _encode(self):
  306. return _clad_to_engine_iface.PlaceOnObject(objectID=self.obj.object_id,
  307. usePreDockPose=self.use_pre_dock_pose)
  308. class PlaceObjectOnGroundHere(action.Action):
  309. '''Tracks the state of the "place object on ground here" action.
  310. Returned by :meth:`~cozmo.robot.Robot.place_object_on_ground_here`
  311. '''
  312. def __init__(self, obj, **kw):
  313. super().__init__(**kw)
  314. #: The object (e.g. an instance of :class:`cozmo.objects.LightCube`) that is being put down
  315. self.obj = obj
  316. def _repr_values(self):
  317. return "object=%s" % (self.obj,)
  318. def _encode(self):
  319. return _clad_to_engine_iface.PlaceObjectOnGroundHere()
  320. class SayText(action.Action):
  321. '''Tracks the progress of a say text robot action.
  322. Returned by :meth:`~cozmo.robot.Robot.say_text`
  323. '''
  324. def __init__(self, text, play_excited_animation, use_cozmo_voice, duration_scalar, voice_pitch, **kw):
  325. super().__init__(**kw)
  326. self.text = text
  327. # Note: play_event must be an AnimationTrigger that supports text-to-speech being generated
  328. if play_excited_animation:
  329. self.play_event = _clad_to_engine_cozmo.AnimationTrigger.OnSawNewNamedFace
  330. else:
  331. # TODO: Switch to use AnimationTrigger.SdkTextToSpeech when that works correctly
  332. self.play_event = _clad_to_engine_cozmo.AnimationTrigger.Count
  333. if use_cozmo_voice:
  334. self.say_style = _clad_to_engine_cozmo.SayTextVoiceStyle.CozmoProcessing_Sentence
  335. else:
  336. # default male human voice
  337. self.say_style = _clad_to_engine_cozmo.SayTextVoiceStyle.Unprocessed
  338. self.duration_scalar = duration_scalar
  339. self.voice_pitch = voice_pitch
  340. def _repr_values(self):
  341. return "text='%s' style=%s event=%s duration=%s pitch=%s" %\
  342. (self.text, self.say_style, self.play_event, self.duration_scalar, self.voice_pitch)
  343. def _encode(self):
  344. return _clad_to_engine_iface.SayText(text=self.text,
  345. playEvent=self.play_event,
  346. voiceStyle=self.say_style,
  347. durationScalar=self.duration_scalar,
  348. voicePitch=self.voice_pitch,
  349. fitToDuration=False)
  350. class SetHeadAngle(action.Action):
  351. '''Represents the Set Head Angle action in progress.
  352. Returned by :meth:`~cozmo.robot.Robot.set_head_angle`
  353. '''
  354. def __init__(self, angle, max_speed, accel, duration, warn_on_clamp, **kw):
  355. super().__init__(**kw)
  356. if angle < MIN_HEAD_ANGLE:
  357. if warn_on_clamp:
  358. logger.warning("Clamping head angle from %s to min %s" % (angle, MIN_HEAD_ANGLE))
  359. self.angle = MIN_HEAD_ANGLE
  360. elif angle > MAX_HEAD_ANGLE:
  361. if warn_on_clamp:
  362. logger.warning("Clamping head angle from %s to max %s" % (angle, MAX_HEAD_ANGLE))
  363. self.angle = MAX_HEAD_ANGLE
  364. else:
  365. self.angle = angle
  366. #: float: Maximum speed of Cozmo's head in radians per second
  367. self.max_speed = max_speed
  368. #: float: Acceleration of Cozmo's head in radians per second squared
  369. self.accel = accel
  370. #: float: Time for Cozmo's head to turn in seconds
  371. self.duration = duration
  372. def _repr_values(self):
  373. return "angle=%s max_speed=%s accel=%s duration=%s" %\
  374. (self.angle, self.max_speed, self.accel, self.duration)
  375. def _encode(self):
  376. return _clad_to_engine_iface.SetHeadAngle(angle_rad=self.angle.radians,
  377. max_speed_rad_per_sec=self.max_speed,
  378. accel_rad_per_sec2=self.accel,
  379. duration_sec=self.duration)
  380. class SetLiftHeight(action.Action):
  381. '''Represents the Set Lift Height action in progress.
  382. Returned by :meth:`~cozmo.robot.Robot.set_lift_height`
  383. '''
  384. def __init__(self, height, max_speed, accel, duration, **kw):
  385. super().__init__(**kw)
  386. if height < 0.0:
  387. logger.warning("lift height %s too small, should be in 0..1 range - clamping", height)
  388. self.lift_height_mm = MIN_LIFT_HEIGHT_MM
  389. elif height > 1.0:
  390. logger.warning("lift height %s too large, should be in 0..1 range - clamping", height)
  391. self.lift_height_mm = MAX_LIFT_HEIGHT_MM
  392. else:
  393. self.lift_height_mm = MIN_LIFT_HEIGHT_MM + (height * (MAX_LIFT_HEIGHT_MM - MIN_LIFT_HEIGHT_MM))
  394. #: float: Maximum speed of Cozmo's lift in radians per second
  395. self.max_speed = max_speed
  396. #: float: Acceleration of Cozmo's lift in radians per second squared
  397. self.accel = accel
  398. #: float: Time for Cozmo's lift to turn in seconds
  399. self.duration = duration
  400. def _repr_values(self):
  401. return "height=%s max_speed=%s accel=%s duration=%s" %\
  402. (self.lift_height_mm, self.max_speed, self.accel, self.duration)
  403. def _encode(self):
  404. return _clad_to_engine_iface.SetLiftHeight(height_mm=self.lift_height_mm,
  405. max_speed_rad_per_sec=self.max_speed,
  406. accel_rad_per_sec2=self.accel,
  407. duration_sec=self.duration)
  408. class TurnInPlace(action.Action):
  409. '''Tracks the progress of a turn in place robot action.
  410. Returned by :meth:`~cozmo.robot.Robot.turn_in_place`
  411. '''
  412. def __init__(self, angle, speed, accel, angle_tolerance, is_absolute, **kw):
  413. super().__init__(**kw)
  414. #: :class:`cozmo.util.Angle`: The angle to turn
  415. self.angle = angle
  416. #: :class:`cozmo.util.Angle`: Angular turn speed (per second).
  417. self.speed = speed
  418. #: :class:`cozmo.util.Angle`: Acceleration of angular turn (per second squared).
  419. self.accel = accel
  420. #: :class:`cozmo.util.Angle`: The minimum angular tolerance to consider
  421. #: the action complete (this is clamped to a minimum of 2 degrees internally).
  422. self.angle_tolerance = angle_tolerance
  423. #: bool: True to turn to a specific angle, False to turn relative to the current pose.
  424. self.is_absolute = is_absolute
  425. def _repr_values(self):
  426. return "angle=%s, speed=%s, accel=%s, tolerance=%s is_absolute=%s" %\
  427. (self.angle, self.speed, self.accel, self.angle_tolerance, self.is_absolute)
  428. def _get_radians(self, in_angle, default_value=0.0):
  429. # Helper method to allow None angles to represent default values
  430. if in_angle is None:
  431. return default_value
  432. else:
  433. return in_angle.radians
  434. def _encode(self):
  435. return _clad_to_engine_iface.TurnInPlace(
  436. angle_rad = self.angle.radians,
  437. speed_rad_per_sec = self._get_radians(self.speed),
  438. accel_rad_per_sec2 = self._get_radians(self.accel),
  439. tol_rad = self._get_radians(self.angle_tolerance),
  440. isAbsolute = int(self.is_absolute))
  441. class PopAWheelie(action.Action):
  442. '''Tracks the progress of a "pop a wheelie" robot action.
  443. Returned by :meth:`~cozmo.robot.Robot.pop_a_wheelie`
  444. '''
  445. def __init__(self, obj, approach_angle, **kw):
  446. super().__init__(**kw)
  447. #: An object (e.g. an instance of :class:`cozmo.objects.LightCube`)
  448. #: being used as leverage to push cozmo on his back
  449. self.obj = obj
  450. if approach_angle is None:
  451. self.use_approach_angle = False
  452. self.approach_angle = util.degrees(0)
  453. else:
  454. self.use_approach_angle = True
  455. self.approach_angle = approach_angle
  456. def _repr_values(self):
  457. return ("object=%s, use_approach_angle=%s, approach_angle=%s" %
  458. (self.obj, self.use_approach_angle, self.approach_angle) )
  459. def _encode(self):
  460. return _clad_to_engine_iface.PopAWheelie(
  461. objectID=self.obj.object_id,
  462. approachAngle_rad=self.approach_angle.radians,
  463. useApproachAngle=self.use_approach_angle,
  464. usePreDockPose=self.use_approach_angle,
  465. useManualSpeed=False)
  466. class TurnTowardsFace(action.Action):
  467. '''Tracks the progress of a turn towards face robot action.
  468. Returned by :meth:`~cozmo.robot.Robot.turn_towards_face`
  469. '''
  470. def __init__(self, face, **kw):
  471. super().__init__(**kw)
  472. #: :class:`~cozmo.faces.Face`: The face to turn towards
  473. self.face = face
  474. def _repr_values(self):
  475. return "face=%s" % (self.face)
  476. def _encode(self):
  477. return _clad_to_engine_iface.TurnTowardsFace(
  478. faceID=self.face.face_id,
  479. maxTurnAngle_rad=util.degrees(180).radians)
  480. class PerformOffChargerContext(event.Dispatcher):
  481. '''A helper class to provide a context manager to do operations while Cozmo is off charger.'''
  482. def __init__(self, robot, **kw):
  483. super().__init__(**kw)
  484. self.robot = robot
  485. async def __aenter__(self):
  486. self.was_on_charger = self.robot.is_on_charger
  487. if self.was_on_charger:
  488. await self.robot.drive_off_charger_contacts(in_parallel=True).wait_for_completed()
  489. return self
  490. async def __aexit__(self, exc_type, exc_value, traceback):
  491. if self.was_on_charger:
  492. await self.robot.backup_onto_charger()
  493. return False
  494. class Robot(event.Dispatcher):
  495. """The interface to a Cozmo robot.
  496. A robot has access to:
  497. * A :class:`~cozmo.world.World` object (:attr:`cozmo.robot.Robot.world`),
  498. which tracks the state of the world the robot knows about
  499. * A :class:`~cozmo.camera.Camera` object (:attr:`cozmo.robot.Robot.camera`),
  500. which provides access to Cozmo's camera
  501. * An Animations object, controlling the playing of animations on the robot
  502. * A Behaviors object, starting and ending robot behaviors such as looking around
  503. Robots are instantiated by the :class:`~cozmo.conn.CozmoConnection` object
  504. and emit a :class:`EvtRobotReady` when it has been configured and is
  505. ready to be commanded.
  506. """
  507. # action factories
  508. _action_dispatcher_factory = action._ActionDispatcher
  509. #: callable: The factory function that returns a
  510. #: :class:`TurnInPlace` class or subclass instance.
  511. turn_in_place_factory = TurnInPlace
  512. #: callable: The factory function that returns a
  513. #: :class:`TurnTowardsFace` class or subclass instance.
  514. turn_towards_face_factory = TurnTowardsFace
  515. #: callable: The factory function that returns a
  516. #: :class:`PickupObject` class or subclass instance.
  517. pickup_object_factory = PickupObject
  518. #: callable: The factory function that returns a
  519. #: :class:`PlaceOnObject` class or subclass instance.
  520. place_on_object_factory = PlaceOnObject
  521. #: callable: The factory function that returns a
  522. #: :class:`GoToPose` class or subclass instance.
  523. go_to_pose_factory = GoToPose
  524. #: callable: The factory function that returns a
  525. #: :class:`GoToObject` class or subclass instance.
  526. go_to_object_factory = GoToObject
  527. #: callable: The factory function that returns a
  528. #: :class:`DockWithCube` class or subclass instance.
  529. dock_with_cube_factory = DockWithCube
  530. #: callable: The factory function that returns a
  531. #: :class:`RollCube` class or subclass instance.
  532. roll_cube_factory = RollCube
  533. #: callable: The factory function that returns a
  534. #: :class:`PlaceObjectOnGroundHere` class or subclass instance.
  535. place_object_on_ground_here_factory = PlaceObjectOnGroundHere
  536. #: callable: The factory function that returns a
  537. #: :class:`PopAWheelie` class or subclass instance.
  538. pop_a_wheelie_factory = PopAWheelie
  539. #: callable: The factory function that returns a
  540. #: :class:`SayText` class or subclass instance.
  541. say_text_factory = SayText
  542. #: callable: The factory function that returns a
  543. #: :class:`SetHeadAngle` class or subclass instance.
  544. set_head_angle_factory = SetHeadAngle
  545. #: callable: The factory function that returns a
  546. #: :class:`SetLiftHeight` class or subclass instance.
  547. set_lift_height_factory = SetLiftHeight
  548. #: callable: The factory function that returns a
  549. #: :class:`DriveOffChargerContacts` class or subclass instance.
  550. drive_off_charger_contacts_factory = DriveOffChargerContacts
  551. #: callable: The factory function that returns a
  552. #: :class:`DriveStraight` class or subclass instance.
  553. drive_straight_factory = DriveStraight
  554. #: callable: The factory function that returns a
  555. #: :class:`DisplayOledFaceImage` class or subclass instance.
  556. display_oled_face_image_factory = DisplayOledFaceImage
  557. # other factories
  558. #: callable: The factory function that returns a
  559. #: :class:`cozmo.anim.Animation` class or subclass instance.
  560. animation_factory = anim.Animation
  561. #: callable: The factory function that returns a
  562. #: :class:`cozmo.anim.AnimationTrigger` class or subclass instance.
  563. animation_trigger_factory = anim.AnimationTrigger
  564. #: callable: The factory function that returns a
  565. #: :class:`cozmo.behavior.Behavior` class or subclass instance.
  566. behavior_factory = behavior.Behavior
  567. #: callable: The factory function that returns a
  568. #: :class:`cozmo.camera.Camera` class or subclass instance.
  569. camera_factory = camera.Camera
  570. #: callable: The factory function that returns a
  571. #: :class:`cozmo.robot.PerformOffChargerContext` class or subclass instance.
  572. perform_off_charger_factory = PerformOffChargerContext
  573. #: callable: The factory function that returns a
  574. #: :class:`cozmo.world.World` class or subclass instance.
  575. world_factory = world.World
  576. # other attributes
  577. #: bool: Set to True if the robot should drive off the charger as soon
  578. #: as the SDK connects to the engine. Defaults to True.
  579. drive_off_charger_on_connect = True # Required for most movement actions
  580. _current_behavior = None # type: Behavior
  581. _is_freeplay_mode_active = False
  582. def __init__(self, conn, robot_id: int, is_primary: bool, **kw):
  583. super().__init__(**kw)
  584. #: :class:`cozmo.conn.CozmoConnection`: The active connection to the engine.
  585. self.conn = conn # type: conn.CozmoConnection
  586. #: int: The internal ID number of the robot.
  587. self.robot_id = robot_id
  588. self._is_ready = False
  589. self._pose = None # type: util.Pose
  590. #: bool: Specifies that this is the primary robot (always True currently)
  591. self.is_primary = is_primary
  592. #: :class:`cozmo.camera.Camera`: Provides access to the robot's camera
  593. self.camera = self.camera_factory(self, dispatch_parent=self)
  594. #: :class:`cozmo.world.World`: Tracks state information about Cozmo's world.
  595. self.world = self.world_factory(self.conn, self, dispatch_parent=self)
  596. self._action_dispatcher = self._action_dispatcher_factory(self)
  597. self._current_face_image_action = None # type: DisplayOledFaceImage
  598. #: :class:`cozmo.util.Speed`: Speed of the left wheel
  599. self.left_wheel_speed = None # type: util.Speed
  600. #: :class:`cozmo.util.Speed`: Speed of the right wheel
  601. self.right_wheel_speed = None # type: util.Speed
  602. self._lift_position = LiftPosition(height=util.distance_mm(MIN_LIFT_HEIGHT_MM))
  603. #: float: The current battery voltage (not linear, but < 3.5 is low)
  604. self.battery_voltage = None # type: float
  605. #: :class:`cozmo.util.Vector3`: The current accelerometer reading (x,y,z)
  606. #: In mm/s^2, measured in Cozmo's head (e.g. x=0 when Cozmo's head is level
  607. #: but x = z = ~7000 mm/s^2 when Cozmo's head is angled 45 degrees up)
  608. self.accelerometer = None # type: util.Vector3
  609. self._is_device_accelerometer_supported = None # type: bool
  610. self._is_device_gyro_supported = None # type: bool
  611. #: :class:`cozmo.util.Vector3`: The current accelerometer reading for
  612. #: the connected mobile device. Requires that you have first called
  613. #: :meth:`enable_device_imu` with `enable_raw = True`. See
  614. #: :attr:`device_accel_user` for a user-filtered equivalent.
  615. self.device_accel_raw = None # type: util.Vector3
  616. #: :class:`cozmo.util.Vector3`: The current user-filtered accelerometer
  617. #: reading for the connected mobile device. Requires that you have first
  618. #: called :meth:`enable_device_imu` with `enable_user = True`. This
  619. #: filtered version removes the constant acceleration from Gravity. See
  620. #: :attr:`device_accel_raw` for a raw version.
  621. self.device_accel_user = None # type: util.Vector3
  622. #: :class:`cozmo.util.Quaternion`: The current gyro reading for
  623. #: the connected mobile device. Requires that you have first called
  624. #: :meth:`enable_device_imu` with `enable_gyro = True`
  625. self.device_gyro = None # type: util.Quaternion
  626. #: :class:`cozmo.util.Vector3`: The current gyro reading (x,y,z)
  627. #: In radians/s, measured in Cozmo's head.
  628. #: Therefore a large value in a given component would indicate Cozmo is
  629. #: being rotated around that axis (where x=forward, y=left, z=up), e.g.
  630. #: y = -5 would indicate that Cozmo is being rolled onto his back
  631. self.gyro = None # type: util.Vector3
  632. #: int: The ID of the object currently being carried (-1 if none)
  633. self.carrying_object_id = -1
  634. #: int: The ID of the object on top of the object currently being carried (-1 if none)
  635. self.carrying_object_on_top_id = -1
  636. #: int: The ID of the object the head is tracking to (-1 if none)
  637. self.head_tracking_object_id = -1
  638. #: int: The ID of the object that the robot is localized to (-1 if none)
  639. self.localized_to_object_id = -1
  640. #: int: The robot's timestamp for the last image seen.
  641. #: ``None`` if no image was received yet.
  642. #: In milliseconds relative to robot epoch.
  643. self.last_image_robot_timestamp = None # type: int
  644. self._pose_angle = None # type: util.Angle
  645. self._pose_pitch = None # type: util.Angle
  646. self._head_angle = None # type: util.Angle
  647. self._robot_status_flags = 0
  648. self._game_status_flags = 0
  649. self._serial_number_head = 0
  650. self._serial_number_body = 0
  651. self._model_number = 0
  652. self._hw_version = 0
  653. # send all received events to the world and action dispatcher
  654. self._add_child_dispatcher(self._action_dispatcher)
  655. self._add_child_dispatcher(self.world)
  656. #### Private Methods ####
  657. def _initialize(self):
  658. # Perform all steps necessary to initialize the robot and trigger
  659. # an EvtRobotReady event when complete.
  660. async def _init():
  661. # Note: Robot state is reset on entering SDK mode, and after any SDK program exits
  662. self.stop_all_motors()
  663. self.enable_all_reaction_triggers(False)
  664. self.enable_stop_on_cliff(True)
  665. self._set_none_behavior()
  666. # Default to no memory map data being streamed
  667. self.world.request_nav_memory_map(-1.0)
  668. # Default to no device IMU data being streamed
  669. self.enable_device_imu(False, False, False)
  670. # Ensure the SDK has full control of cube lights
  671. self._set_cube_light_state(False)
  672. await self.world.delete_all_custom_objects()
  673. # wait for animations to load
  674. await self.conn.anim_names.wait_for_loaded()
  675. msg = _clad_to_engine_iface.GetBlockPoolMessage()
  676. self.conn.send_msg(msg)
  677. self._is_ready = True
  678. logger.info('Robot id=%s serial=%s initialized OK', self.robot_id, self.serial)
  679. self.dispatch_event(EvtRobotReady, robot=self)
  680. self._idle_stack_depth = 0
  681. self.set_idle_animation(anim.Triggers.Count)
  682. asyncio.ensure_future(_init(), loop=self._loop)
  683. def _set_none_behavior(self):
  684. # Internal helper method called from Behavior.stop etc.
  685. msg = _clad_to_engine_iface.ExecuteBehaviorByExecutableType(
  686. behaviorType=_clad_to_engine_cozmo.ExecutableBehaviorType.Wait)
  687. self.conn.send_msg(msg)
  688. if self._current_behavior is not None:
  689. self._current_behavior._set_stopped()
  690. def _set_cube_light_state(self, enable):
  691. msg = _clad_to_engine_iface.EnableLightStates(enable=enable, objectID=-1)
  692. self.conn.send_msg(msg)
  693. def _enable_cube_sleep(self, enable=True, skip_animation=True):
  694. # skip_animation (bool): True to skip the fadeout part of the sleep anim
  695. msg = _clad_to_engine_iface.EnableCubeSleep(enable=enable,
  696. skipAnimation=skip_animation)
  697. self.conn.send_msg(msg)
  698. #### Properties ####
  699. @property
  700. def is_ready(self):
  701. """bool: True if the robot has been initialized and is ready to accept commands."""
  702. return self._is_ready
  703. @property
  704. def anim_names(self):
  705. '''set of string: Set of all the available animation names
  706. An alias of :attr:`cozmo.conn.anim_names`.
  707. Generally animation triggers are preferred over explict animation names:
  708. See :class:`cozmo.anim.Triggers` for available animation triggers.
  709. '''
  710. return self.conn.anim_names
  711. @property
  712. def anim_triggers(self):
  713. '''list of :class:`cozmo.anim.Triggers`, specifying available animation triggers
  714. These can be sent to the play_anim_trigger to make the robot perform animations.
  715. An alias of :attr:`cozmo.anim.Triggers.trigger_list`.
  716. '''
  717. return anim.Triggers.trigger_list
  718. @property
  719. def pose(self):
  720. """:class:`cozmo.util.Pose`: The current pose (position and orientation) of Cozmo
  721. """
  722. return self._pose
  723. @property
  724. def is_moving(self):
  725. '''bool: True if Cozmo is currently moving anything (head, lift or wheels/treads).'''
  726. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_MOVING) != 0
  727. @property
  728. def is_carrying_block(self):
  729. '''bool: True if Cozmo is currently carrying a block.'''
  730. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_CARRYING_BLOCK) != 0
  731. @property
  732. def is_picking_or_placing(self):
  733. '''bool: True if Cozmo is picking or placing something.'''
  734. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_PICKING_OR_PLACING) != 0
  735. @property
  736. def is_picked_up(self):
  737. '''bool: True if Cozmo is currently picked up (in the air).'''
  738. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_PICKED_UP) != 0
  739. @property
  740. def is_falling(self):
  741. '''bool: True if Cozmo is currently falling.'''
  742. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_FALLING) != 0
  743. @property
  744. def is_animating(self):
  745. '''bool: True if Cozmo is currently playing an animation.'''
  746. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_ANIMATING) != 0
  747. @property
  748. def is_animating_idle(self):
  749. '''bool: True if Cozmo is currently playing an idle animation.'''
  750. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_ANIMATING_IDLE) != 0
  751. @property
  752. def is_pathing(self):
  753. '''bool: True if Cozmo is currently traversing a path.'''
  754. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_PATHING) != 0
  755. @property
  756. def is_lift_in_pos(self):
  757. '''bool: True if Cozmo's lift is in the desired position (False if still trying to move there).'''
  758. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.LIFT_IN_POS) != 0
  759. @property
  760. def is_head_in_pos(self):
  761. '''bool: True if Cozmo's head is in the desired position (False if still trying to move there).'''
  762. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.HEAD_IN_POS) != 0
  763. @property
  764. def is_anim_buffer_full(self):
  765. '''bool: True if Cozmo's animation buffer is full (on robot).'''
  766. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_ANIM_BUFFER_FULL) != 0
  767. @property
  768. def is_on_charger(self):
  769. '''bool: True if Cozmo is currently on the charger.'''
  770. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_ON_CHARGER) != 0
  771. @property
  772. def is_charging(self):
  773. '''bool: True if Cozmo is currently charging.'''
  774. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.IS_CHARGING) != 0
  775. @property
  776. def is_cliff_detected(self):
  777. '''bool: True if Cozmo detected a cliff (in front of the robot).'''
  778. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.CLIFF_DETECTED) != 0
  779. @property
  780. def are_wheels_moving(self):
  781. '''bool: True if Cozmo's wheels/treads are currently moving.'''
  782. return (self._robot_status_flags & _clad_to_game_cozmo.RobotStatusFlag.ARE_WHEELS_MOVING) != 0
  783. @property
  784. def is_localized(self):
  785. '''bool: True if Cozmo is localized (i.e. knows where he is with respect to a cube, and has both treads on the ground).'''
  786. return (self._game_status_flags & _clad_to_game_cozmo.GameStatusFlag.IsLocalized) != 0
  787. @property
  788. def pose_angle(self):
  789. ''':class:`cozmo.util.Angle`: Cozmo's pose angle (heading in X-Y plane).'''
  790. return self._pose_angle
  791. @property
  792. def pose_pitch(self):
  793. ''':class:`cozmo.util.Angle`: Cozmo's pose pitch (angle up/down).'''
  794. return self._pose_pitch
  795. @property
  796. def head_angle(self):
  797. ''':class:`cozmo.util.Angle`: Cozmo's head angle (up/down).'''
  798. return self._head_angle
  799. @property
  800. def lift_position(self):
  801. ''':class:`LiftPosition`: The position of Cozmo's lift.'''
  802. return self._lift_position
  803. @property
  804. def lift_height(self):
  805. ''':class:`cozmo.util.Distance`: Height of Cozmo's lift from the ground.
  806. In :const:`MIN_LIFT_HEIGHT` to :const:`MAX_LIFT_HEIGHT` range.
  807. '''
  808. return self._lift_position.height
  809. @property
  810. def lift_ratio(self):
  811. '''float: Ratio from 0 to 1 of how high Cozmo's lift is.'''
  812. return self._lift_position.ratio
  813. @property
  814. def lift_angle(self):
  815. ''':class:`cozmo.util.Angle`: Angle of Cozmo's lift relative to the ground.
  816. In :const:`MIN_LIFT_ANGLE` to :const:`MAX_LIFT_ANGLE` range.
  817. '''
  818. return self._lift_position.angle
  819. @property
  820. def current_behavior(self):
  821. ''':class:`cozmo.behavior.Behavior`: Cozmo's currently active behavior.'''
  822. if self._current_behavior is not None and self._current_behavior.is_active:
  823. return self._current_behavior
  824. else:
  825. return None
  826. @property
  827. def is_behavior_running(self):
  828. '''bool: True if Cozmo is currently running a behavior.
  829. When Cozmo is running a behavior he will behave fairly autonomously
  830. (playing animations and other actions as desired). Attempting to drive
  831. Cozmo whilst in this mode will likely have unexpected behavior on
  832. the robot and confuse Cozmo.
  833. '''
  834. return (self.is_freeplay_mode_active or
  835. (self._current_behavior is not None and self._current_behavior.is_active))
  836. @property
  837. def is_freeplay_mode_active(self):
  838. '''bool: True if Cozmo is in freeplay mode.
  839. When Cozmo is in freeplay mode he will behave autonomously (playing
  840. behaviors, animations and other actions as desired). Attempting to
  841. drive Cozmo whilst in this mode will likely have unexpected behavior
  842. on the robot and confuse Cozmo.
  843. '''
  844. return self._is_freeplay_mode_active
  845. @property
  846. def has_in_progress_actions(self):
  847. '''bool: True if Cozmo has any SDK-triggered actions still in progress.'''
  848. return self._action_dispatcher.has_in_progress_actions
  849. @property
  850. def camera_config(self):
  851. ''':class:`cozmo.robot.CameraConfig`: The read-only config/calibration for this robot's camera
  852. .. deprecated:: 0.12.0
  853. Use: :meth:`cozmo.camera.Camera.config` instead.
  854. '''
  855. warnings.warn("The 'robot.camera_config' method is deprecated, "
  856. "use 'robot.camera.config' instead", DeprecationWarning, stacklevel=2)
  857. return self.camera.config
  858. @property
  859. def serial(self):
  860. '''string: The serial number, as a hex-string (e.g "02e08032"), for the robot.
  861. This matches the Cozmo Serial value in the About section of the settings
  862. menu in the app.
  863. '''
  864. return "%08x" % self._serial_number_body
  865. @property
  866. def is_device_accelerometer_supported(self):
  867. """bool: True if the attached mobile device supports accelerometer data."""
  868. return self._is_device_accelerometer_supported
  869. @property
  870. def is_device_gyro_supported(self):
  871. """bool: True if the attached mobile device supports gyro data."""
  872. return self._is_device_gyro_supported
  873. #### Private Event Handlers ####
  874. #def _recv_default_handler(self, event, **kw):
  875. # msg = kw.get('msg')
  876. # logger_protocol.debug("Robot received unhandled internal event_name=%s kw=%s", event.event_name, kw)
  877. def recv_default_handler(self, event, **kw):
  878. logger.debug("Robot received unhandled public event=%s", event)
  879. def _recv_msg_processed_image(self, _, *, msg):
  880. pass
  881. def _recv_msg_image_chunk(self, evt, *, msg):
  882. self.camera.dispatch_event(evt)
  883. def _recv_msg_current_camera_params(self, evt, *, msg):
  884. self.camera.dispatch_event(evt)
  885. def _recv_msg_robot_observed_motion(self, evt, *, msg):
  886. self.camera.dispatch_event(evt)
  887. def _recv_msg_per_robot_settings(self, evt, *, msg):
  888. self._serial_number_head = msg.serialNumberHead
  889. self._serial_number_body = msg.serialNumberBody
  890. self._model_number = msg.modelNumber
  891. self._hw_version = msg.hwVersion
  892. self.camera._set_config(msg.cameraConfig)
  893. def _recv_msg_unexpected_movement(self, evt, *, msg):
  894. movement_type = UnexpectedMovementType.find_by_id(msg.movementType)
  895. movement_side = UnexpectedMovementSide.find_by_id(msg.movementSide)
  896. self.dispatch_event(EvtUnexpectedMovement, robot=self, timestamp=msg.timestamp,
  897. movement_type=movement_type, movement_side=movement_side)
  898. def _recv_msg_robot_state(self, evt, *, msg):
  899. self._pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z,
  900. q0=msg.pose.q0, q1=msg.pose.q1,
  901. q2=msg.pose.q2, q3=msg.pose.q3,
  902. origin_id=msg.pose.originID)
  903. self._pose_angle = util.radians(msg.poseAngle_rad) # heading in X-Y plane
  904. self._pose_pitch = util.radians(msg.posePitch_rad)
  905. self._head_angle = util.radians(msg.headAngle_rad)
  906. self.left_wheel_speed = util.speed_mmps(msg.leftWheelSpeed_mmps)
  907. self.right_wheel_speed = util.speed_mmps(msg.rightWheelSpeed_mmps)
  908. self._lift_position = LiftPosition(height=util.distance_mm(msg.liftHeight_mm))
  909. self.battery_voltage = msg.batteryVoltage
  910. self.accelerometer = util.Vector3(msg.accel.x, msg.accel.y, msg.accel.z)
  911. self.gyro = util.Vector3(msg.gyro.x, msg.gyro.y, msg.gyro.z)
  912. self.carrying_object_id = msg.carryingObjectID # int_32 will be -1 if not carrying object
  913. self.carrying_object_on_top_id = msg.carryingObjectOnTopID # int_32 will be -1 if no object on top of object being carried
  914. self.head_tracking_object_id = msg.headTrackingObjectID # int_32 will be -1 if head is not tracking to any object
  915. self.localized_to_object_id = msg.localizedToObjectID # int_32 Will be -1 if not localized to any object
  916. self.last_image_robot_timestamp = msg.lastImageTimeStamp
  917. self._robot_status_flags = msg.status # uint_16 as bitflags - See _clad_to_game_cozmo.RobotStatusFlag
  918. self._game_status_flags = msg.gameStatus # uint_8 as bitflags - See _clad_to_game_cozmo.GameStatusFlag
  919. self.dispatch_event(EvtRobotStateUpdated, robot=self)
  920. def _recv_msg_behavior_transition(self, evt, *, msg):
  921. new_type = behavior.BehaviorTypes.find_by_id(msg.newBehaviorExecType)
  922. if self._current_behavior is not None:
  923. if new_type == self._current_behavior.type:
  924. self._current_behavior._on_engine_started()
  925. else:
  926. self._current_behavior._set_stopped()
  927. # Device IMU
  928. def _recv_msg_device_accelerometer_values_raw(self, evt, *, msg):
  929. self.device_accel_raw = util.Vector3(msg.x_gForce, msg.y_gForce, msg.z_gForce)
  930. def _recv_msg_device_accelerometer_values_user(self, evt, *, msg):
  931. self.device_accel_user = util.Vector3(msg.x_gForce, msg.y_gForce, msg.z_gForce)
  932. def _recv_msg_device_gyro_values(self, evt, *, msg):
  933. self.device_gyro = util.Quaternion(msg.w, msg.x, msg.y, msg.z)
  934. def _recv_msg_is_device_imu_supported(self, evt, *, msg):
  935. self._is_device_accelerometer_supported = msg.isAccelerometerSupported
  936. self._is_device_gyro_supported = msg.isGyroSupported
  937. logger.debug("Mobile Device IMU support: accelerometer=%s gyro=%s",
  938. self._is_device_accelerometer_supported, self._is_device_gyro_supported)
  939. #### Public Event Handlers ####
  940. #### Commands ####
  941. def enable_all_reaction_triggers(self, should_enable):
  942. '''Enable or disable Cozmo's responses to being handled or observing the world.
  943. Args:
  944. should_enable (bool): True if the robot should react to its environment.
  945. '''
  946. if should_enable:
  947. msg = _clad_to_engine_iface.RemoveDisableReactionsLock("sdk")
  948. self.conn.send_msg(msg)
  949. else:
  950. msg = _clad_to_engine_iface.DisableAllReactionsWithLock("sdk")
  951. self.conn.send_msg(msg)
  952. def enable_stop_on_cliff(self, enable):
  953. '''Enable or disable Cozmo's ability to drive off a cliff.
  954. Args:
  955. enable (bool): True if the robot should stop moving when a cliff is encountered.
  956. '''
  957. msg = _clad_to_engine_iface.EnableStopOnCliff(enable=enable)
  958. self.conn.send_msg(msg)
  959. def set_robot_volume(self, robot_volume):
  960. '''Set the volume for the speaker in the robot.
  961. Args:
  962. robot_volume (float): The new volume (0.0 = mute, 1.0 = max).
  963. '''
  964. msg = _clad_to_engine_iface.SetRobotVolume(robotId=self.robot_id, volume=robot_volume)
  965. self.conn.send_msg(msg)
  966. def abort_all_actions(self, log_abort_messages=False):
  967. '''Abort all actions on this robot
  968. Args:
  969. log_abort_messages (bool): True to log info on every action that
  970. is aborted.
  971. Abort / Cancel any action that is currently either running or queued within the engine
  972. '''
  973. self._action_dispatcher._abort_all_actions(log_abort_messages)
  974. def enable_facial_expression_estimation(self, enable=True):
  975. '''Enable or Disable facial expression estimation
  976. Cozmo can optionally estimate the facial expression for human faces to
  977. see if he thinks they're happy, sad, etc.
  978. Args:
  979. enable (bool): True to enable facial expression estimation, False to
  980. disable it. By default Cozmo starts with it disabled to save on
  981. processing time.
  982. '''
  983. msg = _clad_to_engine_iface.EnableVisionMode(
  984. mode=_clad_to_engine_cozmo.VisionMode.EstimatingFacialExpression,
  985. enable=enable)
  986. self.conn.send_msg(msg)
  987. def enable_device_imu(self, enable_raw=False, enable_user=False, enable_gyro=False):
  988. """Enable streaming of the connected Mobile devices' IMU data.
  989. The accelerometer and gyro data for the connected phone or tablet can
  990. be streamed from the app to the SDK. You can request any combination of
  991. the 3 data types.
  992. Args:
  993. enable_raw (bool): True to enable streaming of the raw accelerometer
  994. data, which can be accessed via :attr:`device_accel_raw`
  995. enable_user (bool): True to enable streaming of the user-filtered
  996. accelerometer data, which can be accessed via :attr:`device_accel_user`
  997. enable_gyro (bool): True to enable streaming of the gyro
  998. data, which can be accessed via :attr:`device_gyro`
  999. """
  1000. msg = _clad_to_engine_iface.EnableDeviceIMUData(enableAccelerometerRaw=enable_raw,
  1001. enableAccelerometerUser=enable_user,
  1002. enableGyro=enable_gyro)
  1003. self.conn.send_msg(msg)
  1004. def set_needs_levels(self, repair_value=1, energy_value=1, play_value=1):
  1005. """Manually set Cozmo's current needs levels.
  1006. The needs levels control whether Cozmo needs repairing, feeding or playing with.
  1007. Values outside of the 0.0 to 1.0 range are clamped internally.
  1008. Args:
  1009. repair_value (float): How repaired is Cozmo - 0='broken', 1='fully repaired'
  1010. energy_value (float): How energetic is Cozmo - 0='no-energy', 1='full energy'
  1011. play_value (float): How in need of play is Cozmo - 0='bored', 1='happy'
  1012. """
  1013. msg = _clad_to_engine_iface.ForceSetNeedsLevels(
  1014. newNeedLevel = [repair_value, energy_value, play_value])
  1015. self.conn.send_msg(msg)
  1016. ### Camera Commands ###
  1017. def enable_auto_exposure(self):
  1018. '''
  1019. .. deprecated:: 0.12.0
  1020. Use: :meth:`cozmo.camera.Camera.enable_auto_exposure` instead.
  1021. '''
  1022. warnings.warn("The 'robot.enable_auto_exposure' method is deprecated, "
  1023. "use 'robot.camera.enable_auto_exposure' instead.", DeprecationWarning, stacklevel=2)
  1024. self.camera.enable_auto_exposure()
  1025. def set_manual_exposure(self, exposure_ms, gain):
  1026. '''
  1027. .. deprecated:: 0.12.0
  1028. Use: :meth:`cozmo.camera.Camera.set_manual_exposure` instead.
  1029. '''
  1030. warnings.warn("The 'robot.set_manual_exposure' method is deprecated, "
  1031. "use 'robot.camera.set_manual_exposure' instead.", DeprecationWarning, stacklevel=2)
  1032. self.camera.set_manual_exposure(exposure_ms, gain)
  1033. ### Low-Level Commands ###
  1034. def drive_wheel_motors(self, l_wheel_speed, r_wheel_speed,
  1035. l_wheel_acc=None, r_wheel_acc=None):
  1036. '''Tell Cozmo to move his wheels / treads at a given speed.
  1037. The wheels will continue to move at that speed until commanded to drive
  1038. at a new speed, or if :meth:`~cozmo.robot.Robot.stop_all_motors` is called.
  1039. Args:
  1040. l_wheel_speed (float): Speed of the left tread (in millimeters per second)
  1041. r_wheel_speed (float): Speed of the right tread (in millimeters per second)
  1042. l_wheel_acc (float): Acceleration of left tread (in millimeters per second squared)
  1043. ``None`` value defaults this to the same as l_wheel_speed.
  1044. r_wheel_acc (float): Acceleration of right tread (in millimeters per second squared)
  1045. ``None`` value defaults this to the same as r_wheel_speed.
  1046. '''
  1047. if l_wheel_acc is None:
  1048. l_wheel_acc = l_wheel_speed
  1049. if r_wheel_acc is None:
  1050. r_wheel_acc = r_wheel_speed
  1051. msg = _clad_to_engine_iface.DriveWheels(lwheel_speed_mmps=l_wheel_speed,
  1052. rwheel_speed_mmps=r_wheel_speed,
  1053. lwheel_accel_mmps2=l_wheel_acc,
  1054. rwheel_accel_mmps2=r_wheel_acc)
  1055. self.conn.send_msg(msg)
  1056. async def drive_wheels(self, l_wheel_speed, r_wheel_speed,
  1057. l_wheel_acc=None, r_wheel_acc=None, duration=None):
  1058. '''Tell Cozmo to move his wheels / treads at a given speed, and optionally stop them after a given duration.
  1059. If duration is ``None`` then this is equivalent to the non-async
  1060. :meth:`~cozmo.robot.Robot.drive_wheel_motors` method.
  1061. Args:
  1062. l_wheel_speed (float): Speed of the left tread (in millimeters per second).
  1063. r_wheel_speed (float): Speed of the right tread (in millimeters per second).
  1064. l_wheel_acc (float): Acceleration of left tread (in millimeters per second squared).
  1065. ``None`` value defaults this to the same as l_wheel_speed.
  1066. r_wheel_acc (float): Acceleration of right tread (in millimeters per second squared).
  1067. ``None`` value defaults this to the same as r_wheel_speed.
  1068. duration (float): Time for the robot to drive. Will call :meth:`~cozmo.robot.Robot.stop_all_motors`
  1069. after this duration has passed.
  1070. '''
  1071. self.drive_wheel_motors(l_wheel_speed, r_wheel_speed, l_wheel_acc, r_wheel_acc)
  1072. if duration:
  1073. await asyncio.sleep(duration, loop=self._loop)
  1074. self.stop_all_motors()
  1075. def stop_all_motors(self):
  1076. '''Tell Cozmo to stop all motors.'''
  1077. msg = _clad_to_engine_iface.StopAllMotors()
  1078. self.conn.send_msg(msg)
  1079. def move_head(self, speed):
  1080. '''Tell Cozmo's head motor to move with a certain speed.
  1081. Positive speed for up, negative speed for down. Measured in radians per second.
  1082. Args:
  1083. speed (float): Motor speed for Cozmo's head, measured in radians per second.
  1084. '''
  1085. msg = _clad_to_engine_iface.MoveHead(speed_rad_per_sec=speed)
  1086. self.conn.send_msg(msg)
  1087. def move_lift(self, speed):
  1088. '''Tell Cozmo's lift motor to move with a certain speed.
  1089. Positive speed for up, negative speed for down. Measured in radians per second.
  1090. Args:
  1091. speed (float): Motor speed for Cozmo's lift, measured in radians per second.
  1092. '''
  1093. msg = _clad_to_engine_iface.MoveLift()
  1094. msg = _clad_to_engine_iface.MoveLift(speed_rad_per_sec=speed)
  1095. self.conn.send_msg(msg)
  1096. def say_text(self, text, play_excited_animation=False, use_cozmo_voice=True,
  1097. duration_scalar=1.0, voice_pitch=0.0, in_parallel=False, num_retries=0):
  1098. '''Have Cozmo say text!
  1099. Args:
  1100. text (string): The words for Cozmo to say.
  1101. play_excited_animation (bool): Whether to also play an excited
  1102. animation while speaking (moves Cozmo a lot).
  1103. use_cozmo_voice (bool): Whether to use Cozmo's robot voice
  1104. (otherwise, he uses a generic human male voice).
  1105. duration_scalar (float): Adjust the relative duration of the
  1106. generated text to speech audio.
  1107. voice_pitch (float): Adjust the pitch of Cozmo's robot voice [-1.0, 1.0]
  1108. in_parallel (bool): True to run this action in parallel with
  1109. previous actions, False to require that all previous actions
  1110. be already complete.
  1111. num_retries (int): Number of times to retry the action if the
  1112. previous attempt(s) failed.
  1113. Returns:
  1114. A :class:`cozmo.robot.SayText` action object which can be
  1115. queried to see when it is complete
  1116. '''
  1117. action = self.say_text_factory(text=text, play_excited_animation=play_excited_animation,
  1118. use_cozmo_voice=use_cozmo_voice, duration_scalar=duration_scalar,
  1119. voice_pitch=voice_pitch, conn=self.conn,
  1120. robot=self, dispatch_parent=self)
  1121. self._action_dispatcher._send_single_action(action,
  1122. in_parallel=in_parallel,
  1123. num_retries=num_retries)
  1124. return action
  1125. def set_backpack_lights(self, light1, light2, light3, light4, light5):
  1126. '''Set the lights on Cozmo's backpack.
  1127. The light descriptions below are all from Cozmo's perspective.
  1128. Note: The left and right lights only contain red LEDs, so e.g. setting
  1129. them to green will look off, and setting them to white will look red
  1130. Args:
  1131. light1 (:class:`cozmo.lights.Light`): The left backpack light
  1132. light2 (:class:`cozmo.lights.Light`): The front backpack light
  1133. light3 (:class:`cozmo.lights.Light`): The center backpack light
  1134. light4 (:class:`cozmo.lights.Light`): The rear backpack light
  1135. light5 (:class:`cozmo.lights.Light`): The right backpack light
  1136. '''
  1137. msg = _clad_to_engine_iface.SetBackpackLEDs()
  1138. for i, light in enumerate( (light1, light2, light3, light4, light5) ):
  1139. if light is not None:
  1140. lights._set_light(msg, i, light)
  1141. self.conn.send_msg(msg)
  1142. def set_center_backpack_lights(self, light):
  1143. '''Set the lights in the center of Cozmo's backpack to the same color.
  1144. Forces the lights on the left and right to off (this is useful as those
  1145. lights only support shades of red, so cannot generally be set to the
  1146. same color as the center lights).
  1147. Args:
  1148. light (:class:`cozmo.lights.Light`): The lights for Cozmo's backpack.
  1149. '''
  1150. light_arr = [ light ] * 5
  1151. light_arr[0] = lights.off_light
  1152. light_arr[4] = lights.off_light
  1153. self.set_backpack_lights(*light_arr)
  1154. def set_all_backpack_lights(self, light):
  1155. '''Set the lights on Cozmo's backpack to the same color.
  1156. Args:
  1157. light (:class:`cozmo.lights.Light`): The lights for Cozmo's backpack.
  1158. '''
  1159. light_arr = [ light ] * 5
  1160. self.set_backpack_lights(*light_arr)
  1161. def set_backpack_lights_off(self):
  1162. '''Set the lights on Cozmo's backpack to off.'''
  1163. light_arr = [ lights.off_light ] * 5
  1164. self.set_backpack_lights(*light_arr)
  1165. def set_head_light(self, enable):
  1166. '''Turn Cozmo's IR headlight on or off.
  1167. The headlight is on the front of Cozmo's chassis, between his two
  1168. front wheels, underneath his head. Cozmo's camera is IR sensitive
  1169. so although you cannot see the IR light with the naked eye you will
  1170. see it in Cozmo's camera feed.
  1171. Args:
  1172. enable (bool): True turns the light on, False turns it off.
  1173. '''
  1174. msg = _clad_to_engine_iface.SetHeadlight(enable=enable)
  1175. self.conn.send_msg(msg)
  1176. def enable_freeplay_cube_lights(self, enable=True):
  1177. """Enable, or disable, the automatic cube light mode used in freeplay.
  1178. Enabling the freeplay cube light mode causes the cubes to automatically
  1179. pulse blue when Cozmo can see them - as seen in the Cozmo app during
  1180. freeplay mode. This is disabled by default in SDK mode because it
  1181. overrides any other calls to set the cube light colors.
  1182. Args:
  1183. enable (bool): True to enable the freeplay cube light mode,
  1184. False to disable it.
  1185. """
  1186. if enable:
  1187. self._set_cube_light_state(True)
  1188. self._enable_cube_sleep(False, False)
  1189. else:
  1190. self._enable_cube_sleep(True, True)
  1191. self._set_cube_light_state(False)
  1192. def set_head_angle(self, angle, accel=10.0, max_speed=10.0, duration=0.0,
  1193. warn_on_clamp=True, in_parallel=False, num_retries=0):
  1194. '''Tell Cozmo's head to turn to a given angle.
  1195. Args:
  1196. angle: (:class:`cozmo.util.Angle`): Desired angle for
  1197. Cozmo's head. (:const:`MIN_HEAD_ANGLE` to
  1198. :const:`MAX_HEAD_ANGLE`).
  1199. accel (float): Acceleration of Cozmo's head in radians per second squared.
  1200. max_speed (float): Maximum speed of Cozmo's head in radians per second.
  1201. duration (float): Time for Cozmo's head to turn in seconds. A value
  1202. of zero will make Cozmo try to do it as quickly as possible.
  1203. warn_on_clamp (bool): True to log a warning if the angle had to be
  1204. clamped to the valid range (:const:`MIN_HEAD_ANGLE` to
  1205. :const:`MAX_HEAD_ANGLE`).
  1206. in_parallel (bool): True to run this action in parallel with
  1207. previous actions, False to require that all previous actions
  1208. be already complete.
  1209. num_retries (int): Number of times to retry the action if the
  1210. previous attempt(s) failed.
  1211. Returns:
  1212. A :class:`cozmo.robot.SetHeadAngle` action object which can be
  1213. queried to see when it is complete
  1214. '''
  1215. action = self.set_head_angle_factory(angle=angle, max_speed=max_speed,
  1216. accel=accel, duration=duration, warn_on_clamp=warn_on_clamp,
  1217. conn=self.conn, robot=self, dispatch_parent=self)
  1218. self._action_dispatcher._send_single_action(action,
  1219. in_parallel=in_parallel,
  1220. num_retries=num_retries)
  1221. return action
  1222. def set_lift_height(self, height, accel=10.0, max_speed=10.0, duration=0.0,
  1223. in_parallel=False, num_retries=0):
  1224. '''Tell Cozmo's lift to move to a given height
  1225. Args:
  1226. height (float): desired height for Cozmo's lift 0.0 (bottom) to
  1227. 1.0 (top) (we clamp it to this range internally).
  1228. accel (float): Acceleration of Cozmo's lift in radians per
  1229. second squared.
  1230. max_speed (float): Maximum speed of Cozmo's lift in radians per second.
  1231. duration (float): Time for Cozmo's lift to move in seconds. A value
  1232. of zero will make Cozmo try to do it as quickly as possible.
  1233. in_parallel (bool): True to run this action in parallel with
  1234. previous actions, False to require that all previous actions
  1235. be already complete.
  1236. num_retries (int): Number of times to retry the action if the
  1237. previous attempt(s) failed.
  1238. Returns:
  1239. A :class:`cozmo.robot.SetLiftHeight` action object which can be
  1240. queried to see when it is complete.
  1241. '''
  1242. action = self.set_lift_height_factory(height=height, max_speed=max_speed,
  1243. accel=accel, duration=duration, conn=self.conn,
  1244. robot=self, dispatch_parent=self)
  1245. self._action_dispatcher._send_single_action(action,
  1246. in_parallel=in_parallel,
  1247. num_retries=num_retries)
  1248. return action
  1249. ## Animation Commands ##
  1250. def play_audio(self, audio_event):
  1251. '''Sends an audio event to the engine
  1252. Most of these come in pairs, with one to start an audio effect, and one to stop
  1253. if desired.
  1254. Example:
  1255. :attr:`cozmo.audio.AudioEvents.SfxSharedSuccess` starts a sound
  1256. :attr:`cozmo.audio.AudioEvents.SfxSharedSuccessStop` interrupts that sound in progress
  1257. Some events are part of the TinyOrchestra system which have special behavior.
  1258. This system can be intitialized and stopped, and various musical instruments can be
  1259. turned on and off while it is running.
  1260. Args:
  1261. audio_event (object): An attribute of the :class:`cozmo.audio.AudioEvents` class
  1262. '''
  1263. audio_event_id = audio_event.id
  1264. game_object_id = _clad_to_engine_anki.AudioMetaData.GameObjectType.CodeLab
  1265. msg = _clad_to_engine_anki.AudioEngine.Multiplexer.PostAudioEvent(
  1266. audioEvent=audio_event_id, gameObject=game_object_id)
  1267. self.conn.send_msg(msg)
  1268. def play_song(self, song_notes, loop_count=1, in_parallel=False, num_retries=0):
  1269. '''Starts playing song on the robot.
  1270. Plays a provided array of SongNotes using a custom animation on the robot.
  1271. Args:
  1272. song_notes (object[]): An array of :class:`cozmo.song.SongNote` classes
  1273. Returns:
  1274. A :class:`cozmo.anim.Animation` action object which can be queried
  1275. to see when it is complete.
  1276. '''
  1277. msg = _clad_to_engine_iface.ReplaceNotesInSong(notes=song_notes)
  1278. self.conn.send_msg(msg)
  1279. song_animation_name = 'cozmo_sings_custom'
  1280. action = self.animation_factory(song_animation_name, loop_count,
  1281. conn=self.conn, robot=self, dispatch_parent=self)
  1282. self._action_dispatcher._send_single_action(action,
  1283. in_parallel=in_parallel,
  1284. num_retries=num_retries)
  1285. return action
  1286. def play_anim(self, name, loop_count=1, in_parallel=False, num_retries=0,
  1287. ignore_body_track=False, ignore_head_track=False, ignore_lift_track=False):
  1288. '''Starts an animation playing on a robot.
  1289. Returns an Animation object as soon as the request to play the animation
  1290. has been sent. Call the wait_for_completed method on the animation
  1291. if you wish to wait for completion (or listen for the
  1292. :class:`cozmo.anim.EvtAnimationCompleted` event).
  1293. Warning: Specific animations may be renamed or removed in future updates of the app.
  1294. If you want your program to work more reliably across all versions
  1295. we recommend using :meth:`play_anim_trigger` instead.
  1296. Args:
  1297. name (str): The name of the animation to play.
  1298. loop_count (int): Number of times to play the animation.
  1299. in_parallel (bool): True to run this action in parallel with
  1300. previous actions, False to require that all previous actions
  1301. be already complete.
  1302. num_retries (int): Number of times to retry the action if the
  1303. previous attempt(s) failed.
  1304. ignore_body_track (bool): True to ignore the animation track for
  1305. Cozmo's body (i.e. the wheels / treads).
  1306. ignore_head_track (bool): True to ignore the animation track for
  1307. Cozmo's head.
  1308. ignore_lift_track (bool): True to ignore the animation track for
  1309. Cozmo's lift.
  1310. Returns:
  1311. A :class:`cozmo.anim.Animation` action object which can be queried
  1312. to see when it is complete.
  1313. Raises:
  1314. :class:`ValueError` if supplied an invalid animation name.
  1315. '''
  1316. if name not in self.conn.anim_names:
  1317. raise ValueError('Unknown animation name "%s"' % name)
  1318. action = self.animation_factory(name, loop_count,
  1319. ignore_body_track, ignore_head_track, ignore_lift_track,
  1320. conn=self.conn, robot=self, dispatch_parent=self)
  1321. self._action_dispatcher._send_single_action(action,
  1322. in_parallel=in_parallel,
  1323. num_retries=num_retries)
  1324. return action
  1325. def play_anim_trigger(self, trigger, loop_count=1, in_parallel=False,
  1326. num_retries=0, use_lift_safe=False, ignore_body_track=False,
  1327. ignore_head_track=False, ignore_lift_track=False):
  1328. """Starts an animation trigger playing on a robot.
  1329. As noted in the Triggers class, playing a trigger requests that an
  1330. animation of a certain class starts playing, rather than an exact
  1331. animation name as influenced by the robot's mood, and other factors.
  1332. Args:
  1333. trigger (object): An attribute of the :class:`cozmo.anim.Triggers` class
  1334. loop_count (int): Number of times to play the animation
  1335. in_parallel (bool): True to run this action in parallel with
  1336. previous actions, False to require that all previous actions
  1337. be already complete.
  1338. num_retries (int): Number of times to retry the action if the
  1339. previous attempt(s) failed.
  1340. use_lift_safe (bool): True to automatically ignore the lift track
  1341. if Cozmo is currently carrying an object.
  1342. ignore_body_track (bool): True to ignore the animation track for
  1343. Cozmo's body (i.e. the wheels / treads).
  1344. ignore_head_track (bool): True to ignore the animation track for
  1345. Cozmo's head.
  1346. ignore_lift_track (bool): True to ignore the animation track for
  1347. Cozmo's lift.
  1348. Returns:
  1349. A :class:`cozmo.anim.AnimationTrigger` action object which can be
  1350. queried to see when it is complete
  1351. Raises:
  1352. :class:`ValueError` if supplied an invalid animation trigger.
  1353. """
  1354. if not isinstance(trigger, anim._AnimTrigger):
  1355. raise TypeError("Invalid trigger supplied")
  1356. action = self.animation_trigger_factory(trigger, loop_count, use_lift_safe,
  1357. ignore_body_track, ignore_head_track, ignore_lift_track,
  1358. conn=self.conn, robot=self, dispatch_parent=self)
  1359. self._action_dispatcher._send_single_action(action,
  1360. in_parallel=in_parallel,
  1361. num_retries=num_retries)
  1362. return action
  1363. def set_idle_animation(self, anim_trigger):
  1364. '''Set the Idle Animation on Cozmo
  1365. Idle animations keep Cozmo alive inbetween the times other animations play.
  1366. They behave the same as regular animations except that they
  1367. loop forever until another animation is started.
  1368. Args:
  1369. anim_trigger (:class:`cozmo.anim.Triggers`): The animation trigger to set
  1370. Raises:
  1371. :class:`ValueError` if supplied an invalid animation trigger.
  1372. '''
  1373. if not isinstance(anim_trigger, anim._AnimTrigger):
  1374. raise TypeError("Invalid anim_trigger supplied")
  1375. msg = _clad_to_engine_iface.PushIdleAnimation(animTrigger=anim_trigger.id,
  1376. lockName="sdk")
  1377. self.conn.send_msg(msg)
  1378. self._idle_stack_depth += 1
  1379. def clear_idle_animation(self):
  1380. '''Clears any Idle Animation currently playing on Cozmo'''
  1381. msg = _clad_to_engine_iface.RemoveIdleAnimation(lockName="sdk")
  1382. while(self._idle_stack_depth > 0):
  1383. self.conn.send_msg(msg)
  1384. self._idle_stack_depth -= 1
  1385. # Cozmo's Face animation commands
  1386. def display_oled_face_image(self, screen_data, duration_ms,
  1387. in_parallel=True):
  1388. ''' Display a bitmap image on Cozmo's OLED face screen.
  1389. Args:
  1390. screen_data (:class:`bytes`): a sequence of pixels (8 pixels per
  1391. byte) (from e.g.
  1392. :func:`cozmo.oled_face.convert_pixels_to_screen_data`).
  1393. duration_ms (float): time to keep displaying this image on Cozmo's
  1394. face (clamped to 30 seconds in engine).
  1395. in_parallel (bool): True to run this action in parallel with
  1396. previous actions, False to require that all previous actions
  1397. be already complete.
  1398. Returns:
  1399. A :class:`cozmo.robot.DisplayOledFaceImage` action object which
  1400. can be queried to see when it is complete.
  1401. Raises:
  1402. :class:`cozmo.exceptions.RobotBusy` if another action is already
  1403. running and in_parallel==False
  1404. '''
  1405. # We never want 2 face image actions active at once, so clear current
  1406. # face image action (if one is running)
  1407. if ((self._current_face_image_action is not None) and
  1408. self._current_face_image_action.is_running):
  1409. self._current_face_image_action.abort()
  1410. action = self.display_oled_face_image_factory(screen_data=screen_data,
  1411. duration_ms=duration_ms,
  1412. conn=self.conn, robot=self,
  1413. dispatch_parent=self)
  1414. self._current_face_image_action = action
  1415. self._action_dispatcher._send_single_action(action,
  1416. in_parallel=in_parallel,
  1417. num_retries=0)
  1418. return action
  1419. ## Behavior Commands ##
  1420. def start_behavior(self, behavior_type):
  1421. '''Starts executing a behavior.
  1422. Call the :meth:`~cozmo.behavior.Behavior.stop` method on the behavior
  1423. object at some point in the future to terminate execution.
  1424. Args:
  1425. behavior_type (:class:`cozmo.behavior._BehaviorType`): An attribute of
  1426. :class:`cozmo.behavior.BehaviorTypes`.
  1427. Returns:
  1428. :class:`cozmo.behavior.Behavior`
  1429. Raises:
  1430. :class:`TypeError` if an invalid behavior type is supplied.
  1431. '''
  1432. if not isinstance(behavior_type, behavior._BehaviorType):
  1433. raise TypeError('Invalid behavior supplied')
  1434. if self._current_behavior is not None:
  1435. self._current_behavior._set_stopped()
  1436. new_behavior = self.behavior_factory(self, behavior_type,
  1437. is_active=True, dispatch_parent=self)
  1438. msg = _clad_to_engine_iface.ExecuteBehaviorByExecutableType(
  1439. behaviorType=behavior_type.id)
  1440. self.conn.send_msg(msg)
  1441. self._current_behavior = new_behavior
  1442. return new_behavior
  1443. async def run_timed_behavior(self, behavior_type, active_time):
  1444. '''Executes a behavior for a set number of seconds.
  1445. This call blocks and stops the behavior after active_time seconds.
  1446. Args:
  1447. behavior_type (:class:`cozmo.behavior._BehaviorType`): An attribute of
  1448. :class:`cozmo.behavior.BehaviorTypes`.
  1449. active_time (float): specifies the maximum time to execute in seconds
  1450. Raises:
  1451. :class:`TypeError` if an invalid behavior type is supplied.
  1452. '''
  1453. b = self.start_behavior(behavior_type)
  1454. try:
  1455. await b.wait_for_completed(timeout=active_time)
  1456. except asyncio.TimeoutError:
  1457. # It didn't complete within the time, stop it
  1458. b.stop()
  1459. def start_freeplay_behaviors(self):
  1460. '''Start running freeplay behaviors on Cozmo
  1461. Puts Cozmo into a freeplay mode where he autonomously drives around
  1462. and does stuff based on his mood and environment.
  1463. You shouldn't attempt to drive Cozmo during this, as it will clash
  1464. with whatever the current behavior is attempting to do.
  1465. '''
  1466. msg = _clad_to_engine_iface.ActivateHighLevelActivity(
  1467. _clad_to_engine_cozmo.HighLevelActivity.Freeplay)
  1468. self.conn.send_msg(msg)
  1469. self._is_behavior_running = True # The chooser will run them automatically
  1470. self._is_freeplay_mode_active = True
  1471. def stop_freeplay_behaviors(self):
  1472. '''Stop running freeplay behaviors on Cozmo
  1473. Forces Cozmo out of Freeplay mode and stops any currently running
  1474. behaviors and actions.
  1475. '''
  1476. msg = _clad_to_engine_iface.ActivateHighLevelActivity(
  1477. _clad_to_engine_cozmo.HighLevelActivity.Selection)
  1478. self.conn.send_msg(msg)
  1479. self._is_freeplay_mode_active = False
  1480. self._set_none_behavior()
  1481. self.abort_all_actions()
  1482. ## Object Commands ##
  1483. def pickup_object(self, obj, use_pre_dock_pose=True, in_parallel=False,
  1484. num_retries=0):
  1485. '''Instruct the robot to pick up the supplied object.
  1486. Args:
  1487. obj (:class:`cozmo.objects.ObservableObject`): The target object to
  1488. pick up where ``obj.pickupable`` is True.
  1489. use_pre_dock_pose (bool): whether or not to try to immediately pick
  1490. up an object or first position the robot next to the object.
  1491. in_parallel (bool): True to run this action in parallel with
  1492. previous actions, False to require that all previous actions
  1493. be already complete.
  1494. num_retries (int): Number of times to retry the action if the
  1495. previous attempt(s) failed.
  1496. Returns:
  1497. A :class:`cozmo.robot.PickupObject` action object which can be
  1498. queried to see when it is complete.
  1499. Raises:
  1500. :class:`cozmo.exceptions.RobotBusy` if another action is already
  1501. running and in_parallel==False
  1502. :class:`cozmo.exceptions.NotPickupable` if object type can't be picked up.
  1503. '''
  1504. if not obj.pickupable:
  1505. raise exceptions.NotPickupable('Cannot pickup this type of object')
  1506. # TODO: Check with the World to see if Cozmo is already holding an object.
  1507. logger.info("Sending pickup object request for object=%s", obj)
  1508. action = self.pickup_object_factory(obj=obj, use_pre_dock_pose=use_pre_dock_pose,
  1509. conn=self.conn, robot=self, dispatch_parent=self)
  1510. self._action_dispatcher._send_single_action(action,
  1511. in_parallel=in_parallel,
  1512. num_retries=num_retries)
  1513. return action
  1514. def place_on_object(self, obj, use_pre_dock_pose=True, in_parallel=False,
  1515. num_retries=0):
  1516. '''Asks Cozmo to place the currently held object onto a target object.
  1517. Args:
  1518. obj (:class:`cozmo.objects.ObservableObject`): The target object to
  1519. place current held object on, where obj.place_objects_on_this
  1520. is True.
  1521. use_pre_dock_pose (bool): Whether or not to try to immediately pick
  1522. up an object or first position the robot next to the object.
  1523. in_parallel (bool): True to run this action in parallel with
  1524. previous actions, False to require that all previous actions
  1525. be already complete.
  1526. num_retries (int): Number of times to retry the action if the
  1527. previous attempt(s) failed.
  1528. Returns:
  1529. A :class:`cozmo.robot.PlaceOnObject` action object which can be
  1530. queried to see when it is complete.
  1531. Raises:
  1532. :class:`cozmo.exceptions.RobotBusy` if another action is already
  1533. running and in_parallel==False
  1534. :class:`cozmo.exceptions.CannotPlaceObjectsOnThis` if the object cannot have objects
  1535. placed on it.
  1536. '''
  1537. if not obj.place_objects_on_this:
  1538. raise exceptions.CannotPlaceObjectsOnThis('Cannot place objects on this type of object')
  1539. # TODO: Check with the World to see if Cozmo is already holding an object.
  1540. logger.info("Sending place on object request for target object=%s", obj)
  1541. action = self.place_on_object_factory(obj=obj, use_pre_dock_pose=use_pre_dock_pose,
  1542. conn=self.conn, robot=self, dispatch_parent=self)
  1543. self._action_dispatcher._send_single_action(action,
  1544. in_parallel=in_parallel,
  1545. num_retries=num_retries)
  1546. return action
  1547. def place_object_on_ground_here(self, obj, in_parallel=False, num_retries=0):
  1548. '''Ask Cozmo to place the object he is carrying on the ground at the current location.
  1549. Args:
  1550. in_parallel (bool): True to run this action in parallel with
  1551. previous actions, False to require that all previous actions
  1552. be already complete.
  1553. num_retries (int): Number of times to retry the action if the
  1554. previous attempt(s) failed.
  1555. Returns:
  1556. A :class:`cozmo.robot.PlaceObjectOnGroundHere` action object which
  1557. can be queried to see when it is complete.
  1558. Raises:
  1559. :class:`cozmo.exceptions.RobotBusy` if another action is already
  1560. running and in_parallel==False
  1561. '''
  1562. # TODO: Check whether Cozmo is known to be holding the object in question
  1563. logger.info("Sending place down here request for object=%s", obj)
  1564. action = self.place_object_on_ground_here_factory(obj=obj,
  1565. conn=self.conn, robot=self, dispatch_parent=self)
  1566. self._action_dispatcher._send_single_action(action,
  1567. in_parallel=in_parallel,
  1568. num_retries=num_retries)
  1569. return action
  1570. ## Interact with seen Face Commands ##
  1571. def turn_towards_face(self, face, in_parallel=False, num_retries=0):
  1572. '''Tells Cozmo to turn towards this face.
  1573. Args:
  1574. face: (:class:`cozmo.faces.Face`): The face Cozmo will turn towards.
  1575. in_parallel (bool): True to run this action in parallel with
  1576. previous actions, False to require that all previous actions
  1577. be already complete.
  1578. num_retries (int): Number of times to retry the action if the
  1579. previous attempt(s) failed.
  1580. Returns:
  1581. A :class:`cozmo.robot.TurnTowardsFace` action object which can be
  1582. queried to see when it is complete
  1583. '''
  1584. action = self.turn_towards_face_factory(face=face,
  1585. conn=self.conn, robot=self, dispatch_parent=self)
  1586. self._action_dispatcher._send_single_action(action,
  1587. in_parallel=in_parallel,
  1588. num_retries=num_retries)
  1589. return action
  1590. ## Robot Driving Commands ##
  1591. def go_to_pose(self, pose, relative_to_robot=False, in_parallel=False,
  1592. num_retries=0):
  1593. '''Tells Cozmo to drive to the specified pose and orientation.
  1594. If relative_to_robot is set to True, the given pose will assume the
  1595. robot's pose as its origin.
  1596. Since the robot understands position by monitoring its tread movement,
  1597. it does not understand movement in the z axis. This means that the only
  1598. applicable elements of pose in this situation are position.x position.y
  1599. and rotation.angle_z.
  1600. Args:
  1601. pose: (:class:`cozmo.util.Pose`): The destination pose.
  1602. relative_to_robot (bool): Whether the given pose is relative to
  1603. the robot's pose.
  1604. in_parallel (bool): True to run this action in parallel with
  1605. previous actions, False to require that all previous actions
  1606. be already complete.
  1607. num_retries (int): Number of times to retry the action if the
  1608. previous attempt(s) failed.
  1609. Returns:
  1610. A :class:`cozmo.robot.GoToPose` action object which can be queried
  1611. to see when it is complete.
  1612. '''
  1613. if relative_to_robot:
  1614. pose = self.pose.define_pose_relative_this(pose)
  1615. action = self.go_to_pose_factory(pose=pose,
  1616. conn=self.conn, robot=self, dispatch_parent=self)
  1617. self._action_dispatcher._send_single_action(action,
  1618. in_parallel=in_parallel,
  1619. num_retries=num_retries)
  1620. return action
  1621. def go_to_object(self, target_object, distance_from_object,
  1622. in_parallel=False, num_retries=0):
  1623. '''Tells Cozmo to drive to the specified object.
  1624. Args:
  1625. target_object (:class:`cozmo.objects.ObservableObject`): The destination object.
  1626. CustomObject instances are not supported.
  1627. distance_from_object (:class:`cozmo.util.Distance`): The distance from the
  1628. object to stop. This is the distance between the origins. For instance,
  1629. the distance from the robot's origin (between Cozmo's two front wheels)
  1630. to the cube's origin (at the center of the cube) is ~40mm.
  1631. in_parallel (bool): True to run this action in parallel with
  1632. previous actions, False to require that all previous actions
  1633. be already complete.
  1634. num_retries (int): Number of times to retry the action if the
  1635. previous attempt(s) failed.
  1636. Returns:
  1637. A :class:`cozmo.robot.GoToObject` action object which can be queried
  1638. to see when it is complete.
  1639. '''
  1640. if not isinstance(target_object, objects.ObservableObject):
  1641. raise TypeError("Target must be an observable object")
  1642. if isinstance(target_object, objects.CustomObject):
  1643. raise TypeError("CustomObject instances not supported by go_to_object")
  1644. action = self.go_to_object_factory(object_id=target_object.object_id,
  1645. distance_from_object=distance_from_object,
  1646. conn=self.conn, robot=self, dispatch_parent=self)
  1647. self._action_dispatcher._send_single_action(action,
  1648. in_parallel=in_parallel,
  1649. num_retries=num_retries)
  1650. return action
  1651. def dock_with_cube(self, target_object, approach_angle=None,
  1652. alignment_type=robot_alignment.RobotAlignmentTypes.LiftPlate,
  1653. distance_from_marker=None,
  1654. in_parallel=False, num_retries=0):
  1655. '''Tells Cozmo to dock with a specified cube object.
  1656. Args:
  1657. target_object (:class:`cozmo.objects.LightCube`): The cube to dock with.
  1658. approach_angle (:class:`cozmo.util.Angle`): The angle to approach the
  1659. cube from. For example, 180 degrees will cause cozmo to drive
  1660. past the cube and approach it from behind.
  1661. alignment_type (:class:`cozmo.robot_alignment.RobotAlignmentTypes`):
  1662. which part of the robot to line up with the front of the object.
  1663. distance_from_marker (:class:`cozmo.util.Distance`): distance from
  1664. the cube marker to stop when using Custom alignment
  1665. in_parallel (bool): True to run this action in parallel with
  1666. previous actions, False to require that all previous actions
  1667. be already complete.
  1668. num_retries (int): Number of times to retry the action if the
  1669. previous attempt(s) failed.
  1670. Returns:
  1671. A :class:`cozmo.robot.DockWithCube` action object which can be queried
  1672. to see when it is complete.
  1673. '''
  1674. if not isinstance(target_object, objects.LightCube):
  1675. raise TypeError("Target must be a light cube")
  1676. action = self.dock_with_cube_factory(obj=target_object, approach_angle=approach_angle,
  1677. alignment_type=alignment_type, distance_from_marker=distance_from_marker,
  1678. conn=self.conn, robot=self, dispatch_parent=self)
  1679. self._action_dispatcher._send_single_action(action,
  1680. in_parallel=in_parallel,
  1681. num_retries=num_retries)
  1682. return action
  1683. def roll_cube(self, target_object, approach_angle=None, check_for_object_on_top=False,
  1684. in_parallel=False, num_retries=0):
  1685. '''Tells Cozmo to roll a specified cube object.
  1686. Args:
  1687. target_object (:class:`cozmo.objects.LightCube`): The cube to roll.
  1688. approach_angle (:class:`cozmo.util.Angle`): The angle to approach the
  1689. cube from. For example, 180 degrees will cause cozmo to drive
  1690. past the cube and approach it from behind.
  1691. check_for_object_on_top (bool): If there is a cube on top of the
  1692. specified cube, and check_for_object_on_top is True, then Cozmo
  1693. will ignore the action.
  1694. in_parallel (bool): True to run this action in parallel with
  1695. previous actions, False to require that all previous actions
  1696. be already complete.
  1697. num_retries (int): Number of times to retry the action if the
  1698. previous attempt(s) failed.
  1699. Returns:
  1700. A :class:`cozmo.robot.RollCube` action object which can be queried
  1701. to see when it is complete.
  1702. '''
  1703. if not isinstance(target_object, objects.LightCube):
  1704. raise TypeError("Target must be a light cube")
  1705. action = self.roll_cube_factory(obj=target_object, approach_angle=approach_angle,
  1706. check_for_object_on_top=check_for_object_on_top,
  1707. conn=self.conn, robot=self, dispatch_parent=self)
  1708. self._action_dispatcher._send_single_action(action,
  1709. in_parallel=in_parallel,
  1710. num_retries=num_retries)
  1711. return action
  1712. def pop_a_wheelie(self, target_object, approach_angle=None,
  1713. in_parallel=False, num_retries=0):
  1714. '''Tells Cozmo to "pop a wheelie" using a light cube.
  1715. Args:
  1716. target_object (:class:`cozmo.objects.LightCube`): The cube to push
  1717. down on with cozmo's lift, to start the wheelie.
  1718. approach_angle (:class:`cozmo.util.Angle`): The angle to approach the
  1719. cube from. For example, 180 degrees will cause cozmo to drive
  1720. past the cube and approach it from behind.
  1721. in_parallel (bool): True to run this action in parallel with
  1722. previous actions, False to require that all previous actions
  1723. be already complete.
  1724. num_retries (int): Number of times to retry the action if the
  1725. previous attempt(s) failed.
  1726. Returns:
  1727. A :class:`cozmo.robot.PopAWheelie` action object which can be queried
  1728. to see when it is complete.
  1729. '''
  1730. if not isinstance(target_object, objects.LightCube):
  1731. raise TypeError("Target must be a light cube")
  1732. action = self.pop_a_wheelie_factory(obj=target_object,
  1733. approach_angle=approach_angle,
  1734. conn=self.conn,
  1735. robot=self, dispatch_parent=self)
  1736. self._action_dispatcher._send_single_action(action,
  1737. in_parallel=in_parallel,
  1738. num_retries=num_retries)
  1739. return action
  1740. def turn_in_place(self, angle, in_parallel=False, num_retries=0, speed=None,
  1741. accel=None, angle_tolerance=None, is_absolute=False):
  1742. '''Turn the robot around its current position.
  1743. Args:
  1744. angle (:class:`cozmo.util.Angle`): The angle to turn. Positive
  1745. values turn to the left, negative values to the right.
  1746. in_parallel (bool): True to run this action in parallel with
  1747. previous actions, False to require that all previous actions
  1748. be already complete.
  1749. num_retries (int): Number of times to retry the action if the
  1750. previous attempt(s) failed.
  1751. speed (:class:`cozmo.util.Angle`): Angular turn speed (per second).
  1752. accel (:class:`cozmo.util.Angle`): Acceleration of angular turn
  1753. (per second squared).
  1754. angle_tolerance (:class:`cozmo.util.Angle`): angular tolerance
  1755. to consider the action complete (this is clamped to a minimum
  1756. of 2 degrees internally).
  1757. is_absolute (bool): True to turn to a specific angle, False to
  1758. turn relative to the current pose.
  1759. Returns:
  1760. A :class:`cozmo.robot.TurnInPlace` action object which can be
  1761. queried to see when it is complete.
  1762. '''
  1763. action = self.turn_in_place_factory(angle=angle, speed=speed,
  1764. accel=accel, angle_tolerance=angle_tolerance, is_absolute=is_absolute,
  1765. conn=self.conn, robot=self, dispatch_parent=self)
  1766. self._action_dispatcher._send_single_action(action,
  1767. in_parallel=in_parallel,
  1768. num_retries=num_retries)
  1769. return action
  1770. def drive_off_charger_contacts(self, in_parallel=False, num_retries=0):
  1771. '''Tells Cozmo to drive forward slightly to get off the charger contacts.
  1772. All motor movement is disabled while Cozmo is on the charger to
  1773. prevent hardware damage. This command is the one exception and provides
  1774. a way to drive forward a little to disconnect from the charger contacts
  1775. and thereby re-enable all other commands.
  1776. Args:
  1777. in_parallel (bool): True to run this action in parallel with
  1778. previous actions, False to require that all previous actions
  1779. be already complete.
  1780. num_retries (int): Number of times to retry the action if the
  1781. previous attempt(s) failed.
  1782. Returns:
  1783. A :class:`cozmo.robot.DriveOffChargerContacts` action object which
  1784. can be queried to see when it is complete.
  1785. '''
  1786. action = self.drive_off_charger_contacts_factory(conn=self.conn,
  1787. robot=self, dispatch_parent=self)
  1788. self._action_dispatcher._send_single_action(action,
  1789. in_parallel=in_parallel,
  1790. num_retries=num_retries)
  1791. return action
  1792. async def backup_onto_charger(self, max_drive_time=3):
  1793. '''Attempts to reverse robot onto its charger.
  1794. This method assumes the charger is directly behind the robot and
  1795. will keep driving straight back until charger is in contact, or until
  1796. a timeout is reached.
  1797. Args:
  1798. max_drive_time (float): The maximum amount of time in seconds
  1799. to reverse the robot without detecting the charger.
  1800. '''
  1801. await self.drive_wheels(-30, -30)
  1802. timeout = util.Timeout(timeout=max_drive_time)
  1803. while not (timeout.is_timed_out or self.is_on_charger) :
  1804. await asyncio.sleep(0.1, loop=self.loop)
  1805. self.stop_all_motors()
  1806. def perform_off_charger(self):
  1807. '''Returns a context manager to move the robot off of and back onto the charger.
  1808. If the robot is on the charger, it will move a short distance off the contacts,
  1809. perform the code wrapped by the context and then move the robot back onto the
  1810. charger after the wrapped code completes.
  1811. Synchronous example::
  1812. with robot.perform_off_charger():
  1813. action = robot.say_text("Hello")
  1814. action.wait_for_completed()
  1815. Asynchronous example::
  1816. async with robot.perform_off_charger():
  1817. action = robot.say_text("Hello")
  1818. await action.wait_for_completed()
  1819. '''
  1820. return self.perform_off_charger_factory(self)
  1821. def drive_straight(self, distance, speed, should_play_anim=True,
  1822. in_parallel=False, num_retries=0):
  1823. '''Tells Cozmo to drive in a straight line
  1824. Cozmo will drive for the specified distance (forwards or backwards)
  1825. Args:
  1826. distance (:class:`cozmo.util.Distance`): The distance to drive
  1827. (>0 for forwards, <0 for backwards)
  1828. speed (:class:`cozmo.util.Speed`): The speed to drive at
  1829. (should always be >0, the abs(speed) is used internally)
  1830. should_play_anim (bool): Whether to play idle animations
  1831. whilst driving (tilt head, hum, animated eyes, etc.)
  1832. in_parallel (bool): True to run this action in parallel with
  1833. previous actions, False to require that all previous actions
  1834. be already complete.
  1835. num_retries (int): Number of times to retry the action if the
  1836. previous attempt(s) failed.
  1837. Returns:
  1838. A :class:`cozmo.robot.DriveStraight` action object which
  1839. can be queried to see when it is complete.
  1840. '''
  1841. action = self.drive_straight_factory(conn=self.conn,
  1842. robot=self,
  1843. dispatch_parent=self,
  1844. distance=distance,
  1845. speed=speed,
  1846. should_play_anim=should_play_anim)
  1847. self._action_dispatcher._send_single_action(action,
  1848. in_parallel=in_parallel,
  1849. num_retries=num_retries)
  1850. return action
  1851. async def wait_for_all_actions_completed(self):
  1852. '''Waits until all SDK-initiated actions are complete.'''
  1853. await self._action_dispatcher.wait_for_all_actions_completed()
  1854. _UnexpectedMovementSide = collections.namedtuple('_UnexpectedMovementSide', ['name', 'id'])
  1855. class UnexpectedMovementSide(CladEnumWrapper):
  1856. '''Defines the side of collision that caused unexpected movement.
  1857. This will always be UNKNOWN while reaction triggers are disabled.
  1858. Call :meth:`cozmo.robot.Robot.enable_all_reaction_triggers` to enable reaction triggers.
  1859. '''
  1860. _clad_enum = _clad_to_engine_cozmo.UnexpectedMovementSide
  1861. _entry_type = _UnexpectedMovementSide
  1862. #: Unable to tell what side obstructed movement.
  1863. #: Usually caused by reaction triggers being disabled.
  1864. Unknown = _entry_type("Unknown", _clad_enum.UNKNOWN)
  1865. #: Obstruction detected in front of the robot.
  1866. Front = _entry_type("Front", _clad_enum.FRONT)
  1867. #: Obstruction detected behind the robot.
  1868. Back = _entry_type("Back", _clad_enum.BACK)
  1869. #: Obstruction detected to the left of the robot
  1870. Left = _entry_type("Left", _clad_enum.LEFT)
  1871. #: Obstruction detected to the right of the robot
  1872. Right = _entry_type("Right", _clad_enum.RIGHT)
  1873. UnexpectedMovementSide._init_class()
  1874. _UnexpectedMovementType = collections.namedtuple('_UnexpectedMovementType', ['name', 'id'])
  1875. class UnexpectedMovementType(CladEnumWrapper):
  1876. '''Defines the type of unexpected movement.'''
  1877. _clad_enum = _clad_to_engine_cozmo.UnexpectedMovementType
  1878. _entry_type = _UnexpectedMovementType
  1879. #: Tried to turn, but couldn't.
  1880. TurnedButStopped = _entry_type("TurnedButStopped", _clad_enum.TURNED_BUT_STOPPED)
  1881. # Turned in the expected direction, but turned further than expected.
  1882. # Currently unused.
  1883. _TurnedInSameDirection = _entry_type("TurnedInSameDirection", _clad_enum.TURNED_IN_SAME_DIRECTION)
  1884. #: Expected to turn in one direction, but turned the other way.
  1885. #: Also happens when rotation is unexpected.
  1886. TurnedInOppositeDirection = _entry_type("TurnedInOppositeDirection", _clad_enum.TURNED_IN_OPPOSITE_DIRECTION)
  1887. UnexpectedMovementType._init_class()