zaront / vector

C# API for the Anki Vector robot.
MIT License
13 stars 8 forks source link
anki vector vector-robot

Control your Anki Vector with .NET

This API matches the Alpha 0.6.0 release for Python found here\ Vector

Getting Started

Install from nuget

https://www.nuget.org/packages/Vector/

Make sure you have connect your Vector robot to you wifi using the Vector companion app

Vector requires all requests be authorized by an authenticated Anki user\ This script will enable this device to authenticate with your Vector robot for use with a Vector Python SDK program.

Basic code examples

//connect
var robot = new Robot();
await robot.ConnectAsync("[your robot name]"); //example: M9W4

//gain control over the robot by suppressing its personality
robot.StartSuppressingPersonality();
await robot.WaitTillPersonalitySuppressedAsync();

//drive off Charger
await robot.Motors.DriveOffChargerAsync();
await robot.Motors.DriveStraightAsync(50, 50);

//drive in a square
await robot.Motors.DriveStraightAsync(50, 50);
await robot.Motors.TurnInPlaceAsync(1.5708f, 5);
await robot.Motors.DriveStraightAsync(50, 50);
await robot.Motors.TurnInPlaceAsync(1.5708f, 5);
await robot.Motors.DriveStraightAsync(50, 50);
await robot.Motors.TurnInPlaceAsync(1.5708f, 5);
await robot.Motors.DriveStraightAsync(50, 50);
await robot.Motors.TurnInPlaceAsync(1.5708f, 5);

//play an animation
await robot.Animation.PlayAsync("anim_vc_laser_lookdown_01");

//say something
await robot.Audio.SayTextAsync("all done");

//disconnect
await robot.DisconnectAsync();

First time connection

If its your first time connection to your vector with this API you will need to grant your device API access

//grant this device api access - one time only
await robot.GrantApiAccessAsync("[your robot Name]", "[ip address]", "[serial num]", "[user account]", "[password]");

by default the connection info is stored automaticly for you and you can reconnect using

await robot.ConnectAsync("[your robot name]");

For up to a year before your authentication token in the connection info expires, and you will need to grant new access.

Suppress its personality

Sometimes you want Vector to act like a zombie drone and do exactly what you say. to do this you can suppress its personality

robot.OnSuppressPersonality += (sender, e) =>
{
    //do stuff each time its personality is suppressed
};
robot.StartSuppressingPersonality();

Text to speach

Saying something is as simple as robot.Audio.SayTextAsync("all done"). However some commands require suppressing the robots personality before you can have it accept your commands. Speaking is one of them. If you are trying to speak shortly after connecting to the robot, you will need to insure that its personality is suppress first.

var robot = new Robot();
await robot.ConnectAsync("A5A7");
robot.SuppressPersonalityAsync().ThrowFeedException(); //attempt to suppress its personality
await robot.WaitTillPersonalitySuppressedAsync(); //wait intil its personality is suppressed
await robot.Audio.SayTextAsync("all done");  //now you are free to talk

Get the camera feed

The camera feed comes back as a stream of System.Drawing.Image

Return on same thread
robot.Camera.ImageReceived += (sender, e) =>
{
    //show my image.  NOTE: on the *same* thread
};
robot.Camera.CameraFeedAsync().ThrowFeedException();
Return on its own thread
robot.Camera.ImageReceived += (sender, e) =>
{
    //show my image.  NOTE: on a *diffrent* thread
};
robot.Camera.StartCameraFeed();

About events

All events comming back from the robot come through an async feed. you must activate these feeds to start receving events.
These include events for getting the robots position changes, internal map changes, camera, voice and object recognition and others. All event feeds have an async way to start the feed, and a syncronous way to start the feed. the sycronous ways starts it own thread to manage the events. This means that the event handler will be called on a diffrent thread. All syncronous methods to start event feeds begin with Start and Stop

example: robot.StartEventListening() and robot.StopEventListening()

While async methods are missing the start and stop prefix

example: robot.EventListeningAsync()

Running the async version of the event feed means that event handlers will be call on the same thread. When running the async event feed its inportant to handle exceptions, otherwize you will not be notified when the feed shuts down. You can chain togeather event handlers using the .ContinueWith method on the task. I recomend placing .ThrowFeedException() at the end of your exception handling chain. This will throw an exception for any remaining unhandled exceptions.

The .ThrowFeedException() is a task extention method you can enabled with using Vector; namespace

How to run everything on a single thread

To start all event handlers on the same thread call

//start all event listeners on the *same* thread
robot.SuppressPersonalityAsync().ThrowFeedException();
robot.Camera.CameraFeedAsync().ContinueWith(RestartCamera).ThrowFeedException();
robot.World.MapFeedAsync().ThrowFeedException();
robot.EventListeningAsync().ThrowFeedException();
robot.Audio.AudioFeedAsync().ThrowFeedException();

To start all event handlers on their own thread call

//start all event listeners on *their own* thread
robot.StartSuppressingPersonality();
robot.Camera.StartCameraFeed();
robot.World.StartMapFeed();
robot.StartEventListening();
robot.Audio.StartAudioFeed();

How to call things without async

Almost every signature in the API is a async method. Its highly recomended to use the async signature, but if you want you can call an async method syncronously

//works well if your method has no return value
robot.Audio.SayTextAsync("synchronous method call").Wait();

//works well if you method returns something
var animations = robot.Animation.ListAsync().Result;

Show an image on the screen

images are automaticly resized

robot.Screen.SetScreenImage(@"c:\mycoolimage.jpg");

Download and play a gif on the screen

var client = new WebClient();
client.DownloadFile("https://media1.giphy.com/media/5kq0GCjHA8Rwc/giphy.gif", "video.gif");
var video = Image.FromFile("video.gif");
var dimension = new FrameDimension(video.FrameDimensionsList[0]);
for (int loop = 0; loop < 5; loop++)
{
    for (int frame = 0; frame < video.GetFrameCount(dimension); frame++)
    {
        video.SelectActiveFrame(dimension, frame);
        await robot.Screen.SetScreenImage(video);
    }
}

Recognize custom markers

You size and print these custom markers then you can tell vector to recognize them. here is how:

robot.World.OnObjectObserved += (sender, e) =>
{
    if (e.Object.Name == "My Marker")
    {
        //Found my marker
    }
};
await robot.World.AddWallMarkerAsync("My Marker", ObjectMarker.Circles2, true, 30, 30, 30, 30); //register a 30mm custom marker
robot.EventListeningAsync().ThrowFeedException(); //start listening for recognized markers

How to regenerate the gRPC

Vector uses a gRPC service definition to communicate. You can regenerate a c# library (Vector.Communication) that matches their service definition using .proto files. This may need to be regenerated as their firmware evolves for their robot.

To do this, first install this extention for Visual Studio:\ Protobuf Generator\ This will install the gRPC generator tools into your nuget package location.

Next, in the same parent directory that you have cloned this repository, also clone the following:\ git clone https://github.com/googleapis/googleapis.git\ git clone https://github.com/anki/vector-python-sdk.git

Now you can run the following script to regenerate the files in the Vector.Communication project\ GenerateRPC.bat

I'm always happy to accept pull requests or Fix issues

enjoy!