Aller au contenu principal
Retour au blogue

Développement logiciel

Comment interagir avec Spot SDK

Robin Kurtz
08 févr. 2022 ∙ 6 mins
Le robot Sport en train d'explorer un immeuble industriel

Comme vous le savez sans doute, nous sommes les fiers propriétaires d'un robot Spot. Ce fut un plaisir d'explorer cette nouvelle technologie et de découvrir comment elle peut combler les besoins de nos clients. Au fur et à mesure que nous en apprenons davantage sur Spot, nous voulons partager avec vous nos découvertes, et tout particulièrement la façon dont vous pouvez commander à Spot d’effectuer des tâches dans le contrôleur. Nous pouvons facilement y entrer avec l'aide de Spot SDK (software development kit) et du Knowledge Center fournis par Boston Dynamics.

Communiquer avec Spot

Premièrement, nous devons établir une connexion sécurisée avec Spot afin de le commander. Spot propose différentes méthodes, pour de meilleurs résultats, nous vous recommandons de vous connecter à son réseau Wifi 2,4 GHz. Notez que si vous faites la même chose, il serait idéal d'avoir plusieurs connexions Wifi (ou LAN) sur votre machine, car vous devrez probablement maintenir une connexion à votre Internet pour télécharger des ressources ou demander de l'aide en cours de route.

Une fois authentifié, nous pouvons maintenant envoyer un ping à son adresse IP avec succès.

$ ping 192.168.80.3
PING 192.168.80.3 (192.168.80.3) 56(84) bytes of data.
64 bytes from 192.168.80.3: icmp_seq=1 ttl=64 time=5.23 ms
64 bytes from 192.168.80.3: icmp_seq=2 ttl=64 time=3.04 ms

Pour commander Spot, vous aurez également besoin d'un compte utilisateur et d'un mot de passe fonctionnels. Nous utiliserons des espaces réservés, super sécurisés, pour cet article.

Configuration de l'environnement

Techniquement, vous pouvez exécuter notre code python directement sur votre machine. Cependant, chez Osedea, nous aimons utiliser Docker, avec l'aide de Visual Studio Code Remote - Containers, c'est un rêve !

$ mkdir hello_spot
$ cd hello_spot
$ touch Dockerfile
$ touch docker-requirements.txt
$ touch hello_spot.py

Tout d'abord, notre Dockerfile doit contenir les éléments suivants:

FROM python:3.7-slim

COPY docker-requirements.txt .
RUN python3 -m pip install -r docker-requirements.txt

COPY . /app/
WORKDIR /app

ENTRYPOINT ["python3", "/app/hello_spot.py"]

Et notre docker-requirements.txt contiendra les bibliothèques dont nous avons besoin pour communiquer avec Spot :

bosdyn-api==3.0.0
bosdyn-client==3.0.0
bosdyn-core==3.0.0

Maintenant, à partir de VSCode « Open Folder in Container » et laissez la magie opérer. Une fois le conteneur créé, vous pouvez confirmer que vous pouvez toujours vous connecter à Spot en envoyant une fois de plus un ping à son adresse IP :

$ ping 192.168.80.3

Commander Spot

Maintenant que nous avons établi une connexion avec Spot et mis en place un environnement de travail, nous pouvons commencer !

import argparse
import sys
import time

import bosdyn.client
import bosdyn.client.lease
import bosdyn.client.util
import bosdyn.geometry
from bosdyn.client.robot_command import (RobotCommandBuilder,
                                         RobotCommandClient, blocking_stand)


def hello_spot(options):
    """A simple example of using the Boston Dynamics API to command a Spot robot."""

    # The SDK object is the primary entry point to the Boston Dynamics API.
    # create_standard_sdk will initialize an SDK object with typical default
    # parameters. The argument passed in is a string identifying the client.
    sdk = bosdyn.client.create_standard_sdk('HelloSpotClient')

    # A Robot object represents a single robot. Clients using the Boston
    # Dynamics API can manage multiple robots, but this tutorial limits
    # access to just one. The network address of the robot needs to be
    # specified to reach it. This can be done with a DNS name
    # (e.g. spot.intranet.example.com) or an IP literal (e.g. 10.0.63.1)
    robot = sdk.create_robot(options.hostname)

    # Clients need to authenticate to a robot before being able to use it.
    robot.authenticate(options.username, options.password)

    # Establish time sync with the robot. This kicks off a background thread to establish time sync.
    # Time sync is required to issue commands to the robot. After starting time sync thread, block
    # until sync is established.
    robot.time_sync.wait_for_sync()

    # Verify the robot is not estopped and that an external application has registered and holds
    # an estop endpoint.
    assert not robot.is_estopped(), "Robot is estopped. Please use an external E-Stop client, " \
                                    "such as the estop SDK example, to optionsure E-Stop."

    # Only one client at a time can operate a robot. Clients acquire a lease to
    # indicate that they want to control a robot. Acquiring may fail if another
    # client is currently controlling the robot. When the client is done
    # controlling the robot, it should return the lease so other clients can
    # control it. Note that the lease is returned as the "finally" condition in this
    # try-catch-finally block.
    lease_client = robot.ensure_client(
        bosdyn.client.lease.LeaseClient.default_service_name)
    lease = lease_client.acquire()
    try:
        with bosdyn.client.lease.LeaseKeepAlive(lease_client):
            # Now, we are ready to power on the robot. This call will block until the power
            # is on. Commands would fail if this did not happen. We can also check that the robot is
            # powered at any point.
            robot.power_on(timeout_sec=20)
            assert robot.is_powered_on(), "Robot power on failed."

            # Tell the robot to stand up. The command service is used to issue commands to a robot.
            # The set of valid commands for a robot depends on hardware optionsuration. See
            # SpotCommandHelper for more detailed examples on command building. The robot
            # command service requires timesync between the robot and the client.
            command_client = robot.ensure_client(
                RobotCommandClient.default_service_name)
            blocking_stand(command_client, timeout_sec=10)
            time.sleep(3)

            # Tell the robot to stand in a twisted position.
            #
            # The RobotCommandBuilder constructs command messages, which are then
            # issued to the robot using "robot_command" on the command client.
            #
            # In this example, the RobotCommandBuilder generates a stand command
            # message with a non-default rotation in the footprint frame. The footprint
            # frame is a gravity aligned frame with its origin located at the geometric
            # center of the feet. The X axis of the footprint frame points forward along
            # the robot's length, the Z axis points up aligned with gravity, and the Y
            # axis is the cross-product of the two.
            footprint_R_body = bosdyn.geometry.EulerZXY(
                yaw=0.4, roll=0.0, pitch=0.0)
            cmd = RobotCommandBuilder.synchro_stand_command(
                footprint_R_body=footprint_R_body)
            command_client.robot_command(cmd)
            time.sleep(3)

            # Now tell the robot to stand taller, using the same approach of constructing
            # a command message with the RobotCommandBuilder and issuing it with
            # robot_command.
            cmd = RobotCommandBuilder.synchro_stand_command(body_height=0.1)
            command_client.robot_command(cmd)
            time.sleep(3)

            # Power the robot off. By specifying "cut_immediately=False", a safe power off command
            # is issued to the robot. This will attempt to sit the robot before powering off.
            robot.power_off(cut_immediately=False, timeout_sec=20)
            assert not robot.is_powered_on(), "Robot power off failed."
    finally:
        # If we successfully acquired a lease, return it.
        lease_client.return_lease(lease)


def main(argv):
    """Command line interface."""
    parser = argparse.ArgumentParser()
    bosdyn.client.util.add_common_arguments(parser)
    options = parser.parse_args(argv)
    try:
        hello_spot(options)
        return True
    except Exception as exc:
        logger = bosdyn.client.util.get_logger()
        logger.error(f"Hello, Spot! threw an exception: {str(exc)}")
        return False


if __name__ == '__main__':
    if not main(sys.argv[1:]):
        sys.exit(1)

Notez que l'extrait ci-dessus a été extrait du SDK mentionné ci-haut et supprimé. Nous vous invitons à jeter un œil à un exemple plus fonctionnel de hello spot.py.

Une fois cela fait, l'exécution de la commande suivante active Spot et le fait se tenir debout, poser, se tenir plus grand, puis s'éteindre [1].

$ export USERNAME=osedea
$ export PASSWORD=secret
$ export ROBOT_IP=192.168.80.3

$ python3 hello_spot.py --username USERNAME --password PASSWORD ROBOT_IP

Félicitations 🎉 ! Vous avez en théorie commandé avec succès votre Spot… cependant, si vous n'en avez pas, envoyez-nous un message et peut-être pourrez-vous rencontrer le nôtre.

[1] Pour ce faire vous devez techniquement établir une estop externe. Cela peut facilement se faire en suivant l’exemple suivant : Boston-Dynamic/spot-sdk

Crédit Photo: yakari_pixel