Geometry

In this chapter, we explain about Geometry. Geometry manages how AUTD3 devices are placed in the real world.

Connect multiple devices

AUTD3 devices can be connected to each other via the daisy chain. SDK is designed to be used transparently even if multiple devices are connected.

To use multiple devices, connect the PC and the EtherCAT In of the first device with an Ethernet cable, and connect the EtherCAT Out of the -th device and the EtherCAT In of the -th device with an Ethernet cable (See Concept).

In SDK, you must call add_device function in the order of the connected devices when using multiple devices.

For example, suppose you have two devices as shown in the figure above. The left device is the first device, and the right device is the second device. Then, the code is as follows.

use autd3::prelude::*;

#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let _autd = 
Controller::builder([
        AUTD3::new(Point3::origin()), 
        AUTD3::new(Point3::new(AUTD3::DEVICE_WIDTH, 0., 0.))
    ])
   .open(autd3::link::Nop::builder())?;
Ok(())
}
#include<chrono>
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
autd3::ControllerBuilder({autd3::AUTD3(autd3::Point3::origin()),
                          autd3::AUTD3(autd3::Point3(autd3::AUTD3::DEVICE_WIDTH,
                                                     0, 0))})
    ;
    return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Utils;
Controller.Builder([
   new AUTD3(Point3.Origin),
   new AUTD3(new Point3(AUTD3.DeviceWidth, 0, 0))
])
;
from pyautd3 import AUTD3, Controller

Controller.builder(
    [AUTD3([0.0, 0.0, 0.0]), AUTD3([AUTD3.DEVICE_WIDTH, 0.0, 0.0])],
)

Here, the first argument of the AUTD3 constructor is the position, and the second argument is the rotation. The rotation is specified by ZYZ Euler angles or quaternions. Also, AUTD3::DEVICE_WIDTH is the width of the device (including the outline of the board). In this example, no rotation is performed, so the second argument can be zero.

And, for example, suppose you have two devices as shown in the figure above, where the global origin is set to the left device. Then, the code is as follows.

use autd3::prelude::*;

#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let _autd = 
Controller::builder([
        AUTD3::new(Point3::new(-AUTD3::DEVICE_WIDTH, 0., 0.)),
        AUTD3::new(Point3::origin())
    ])
   .open(autd3::link::Nop::builder())?;
Ok(())
}
#include<chrono>
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
autd3::ControllerBuilder(
    {autd3::AUTD3(autd3::Point3(-autd3::AUTD3::DEVICE_WIDTH, 0, 0)),
     autd3::AUTD3(autd3::Point3::origin())})
    ;
    return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Utils;
Controller.Builder([
   new AUTD3(new Point3(-AUTD3.DeviceWidth, 0, 0)),
   new AUTD3(Point3.Origin)
])
;
from pyautd3 import AUTD3, Controller
Controller.builder(
    [
        AUTD3([-AUTD3.DEVICE_WIDTH, 0.0, 0.0]),
        AUTD3([0.0, 0.0, 0.0]),
    ],
)

Furthermore, for example, suppose you have two devices as shown in the figure above, where the global origin is set to the lower device. Then, the code is as follows.

use autd3::prelude::*;

#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let _autd = 
Controller::builder([
        AUTD3::new(Point3::origin()),
        AUTD3::new(Point3::new(0., 0., AUTD3::DEVICE_WIDTH))
            .with_rotation(EulerAngle::ZYZ(0. * rad, PI/2.0 * rad, 0. * rad))
    ])
   .open(autd3::link::Nop::builder())?;
Ok(())
}
#include<chrono>
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
using autd3::pi;
using autd3::rad;
autd3::ControllerBuilder(
    {autd3::AUTD3(autd3::Point3::origin()),
     autd3::AUTD3(autd3::Point3(autd3::AUTD3::DEVICE_WIDTH, 0, 0))
         .with_rotation(autd3::EulerAngles::ZYZ(0 * rad, pi / 2 * rad,
                                                0 * rad))})
    ;
    return 0; }
using System;
using AUTD3Sharp;
using AUTD3Sharp.Utils;
using static AUTD3Sharp.Units;
Controller.Builder([
   new AUTD3(Point3.Origin),
   new AUTD3(new Point3(AUTD3.DeviceWidth, 0, 0))
         .WithRotation(EulerAngles.Zyz(0 * rad, MathF.PI / 2 * rad, 0 * rad))
])
;
import numpy as np
from pyautd3 import AUTD3, Controller, EulerAngles, rad
Controller.builder(
    [
        AUTD3([0.0, 0.0, 0.0]),
        AUTD3([AUTD3.DEVICE_WIDTH, 0.0, 0.0]).with_rotation(
            EulerAngles.ZYZ(0 * rad, np.pi / 2 * rad, 0 * rad),
        ),
    ],
)

Device/Transducer index

Devices are assigned indices starting from 0 in the order in which they are connected to the PC.

Also, each device has 249 transducers, and local indices are assigned (see the concept for the surface photo of AUTD).

Geometry API

  • num_devices: Get the number of devices
  • num_transducers: Get the number of all transducers
  • center: Get the center of all transducers
use autd3::prelude::*;

#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let autd = Controller::builder([AUTD3::new(Point3::origin())])
   .open(autd3::link::Nop::builder())?;
let num_dev = autd.num_devices();
let num_tr = autd.num_transducers();
let center = autd.center();
Ok(())
}
#include<chrono>
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
auto autd =
autd3::ControllerBuilder({autd3::AUTD3(autd3::Point3::origin())}).open(autd3::link::Nop::builder());
const auto num_dev = autd.num_devices();
const auto num_tr = autd.num_transducers();
const auto center = autd.center();
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Utils;
using var autd = Controller.Builder([new AUTD3(Point3.Origin)]).Open(Nop.Builder());
var numDevices = autd.NumDevices;
var numTransducers = autd.NumTransducers;
var center = autd.Center;
from pyautd3 import AUTD3, Controller
from pyautd3.link.audit import Audit

autd = Controller.builder([AUTD3([0.0, 0.0, 0.0])]).open(Audit.builder())
num_devices = autd.num_devices
num_transducers = autd.num_transducers
center = autd.center

Device access

Geometry is a container of Device.

To access Device, use indexer. Or, you can use an iterator.

use autd3::prelude::*;

#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let autd = Controller::builder([AUTD3::new(Point3::origin())])
   .open(autd3::link::Nop::builder())?;
let dev = &autd[0];
for dev in &autd {
    // do something
}
Ok(())
}
#include<chrono>
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
auto autd =
autd3::ControllerBuilder({autd3::AUTD3(autd3::Point3::origin())}).open(autd3::link::Nop::builder());
{
auto dev = autd[0];
}
{
for (auto& dev : autd) {
  // do something
}
}
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Utils;
using var autd = Controller.Builder([new AUTD3(Point3.Origin)]).Open(Nop.Builder());
{
  var dev = autd[0];
  }
foreach (var dev in autd)
{
  // do something
}
from pyautd3 import AUTD3, Controller
from pyautd3.link.audit import Audit

autd = Controller.builder([AUTD3([0.0, 0.0, 0.0])]).open(Audit.builder())
dev = autd[0]
for _dev in autd:
    pass

Device API

  • idx: Index of the device
  • enable: Enable flag. If it is off, the data of the device will not be updated.
    • Note that this only controls whether the data is updated or not, and does not stop the output.
  • sound_speed: Get/set the speed of sound. The unit is mm/s.
  • set_sound_speed_from_temp: Set the sound speed from the temperature. The unit of the temperature is Celsius. The default sound speed is , which corresponds to the sound speed of air at about 15 degrees Celsius. Note that there is a function with the same name in Geometry, and you can set the sound speed from the temperature for all devices by using it.
  • attenuation: Get/set the attenuation coefficient. The unit is Np/mm.
  • translate: Apply translation
  • rotate: Apply rotation
  • affine: Apply affine transformation (translation/rotation)
use autd3::prelude::*;

#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut autd = Controller::builder([AUTD3::new(Point3::origin())])
   .open(autd3::link::Nop::builder())?;
let dev = &mut autd[0];
let idx = dev.idx();
dev.enable = false;
dev.sound_speed = 340e3;
dev.set_sound_speed_from_temp(15.);
let t = Vector3::new(1., 0., 0.);
let r = UnitQuaternion::from_quaternion(Quaternion::new(1., 0., 0., 0.));
dev.translate(t);
dev.rotate(r);
dev.affine(t, r);
let wavelength = dev.wavelength();
let wavenumber = dev.wavenumber();
let rotation = dev.rotation();
let x_dir = dev.x_direction();
let y_dir = dev.y_direction();
let axial_dir = dev.axial_direction();
Ok(())
}
#include<chrono>
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
auto autd =
autd3::ControllerBuilder({autd3::AUTD3(autd3::Point3::origin())}).open(autd3::link::Nop::builder());
auto dev = autd[0];
const auto idx = dev.idx();
const auto enable = dev.enable();
dev.set_enable(false);
const auto sound_speed = dev.sound_speed();
dev.set_sound_speed(340e3);
dev.set_sound_speed_from_temp(15.);
const auto t = autd3::Vector3(1., 0., 0.);
const auto r = autd3::Quaternion(1., 0., 0., 0.);
dev.translate(t);
dev.rotate(r);
dev.affine(t, r);
const auto wavelength = dev.wavelength();
const auto wavenumber = dev.wavenumber();
const auto rotation = dev.rotation();
const auto x_dir = dev.x_direction();
const auto y_dir = dev.y_direction();
const auto axial_dir = dev.axial_direction();
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Utils;
using var autd = Controller.Builder([new AUTD3(Point3.Origin)]).Open(Nop.Builder());
var dev = autd[0];
var idx = dev.Idx;
dev.Enable = false;
dev.SoundSpeed = 340e3f;
dev.SetSoundSpeedFromTemp(15);
var t = new Vector3(1, 0, 0);
var r = new Quaternion(0, 0, 0, 1);
dev.Translate(t);
dev.Rotate(r);
dev.Affine(t, r);
var wavelength = dev.Wavelength;
var wavenumber = dev.Wavenumber;
var rotation = dev.Rotation;
var xDir = dev.XDirection;
var yDir = dev.YDirection;
var axialDir = dev.AxialDirection;
import numpy as np
from pyautd3 import AUTD3, Controller
from pyautd3.link.audit import Audit

autd = Controller.builder([AUTD3([0.0, 0.0, 0.0])]).open(Audit.builder())
dev = autd[0]
idx = dev.idx
dev.enable = False
dev.sound_speed = 340e3
dev.set_sound_speed_from_temp(15.0)
t = np.array([1.0, 0.0, 0.0])
r = np.array([1.0, 0.0, 0.0, 0.0])
dev.translate(t)
dev.rotate(r)
dev.affine(t, r)
wavelength = dev.wavelength
wavenumber = dev.wavenumber
rotation = dev.rotation
x_dir = dev.x_direction
y_dir = dev.y_direction
axial_dir = dev.axial_direction

Transducer access

Device is a container of Transducer, and Transducer contains information of each transducer.

To access Transducer, use the indexer. Or, you can use an iterator.

use autd3::prelude::*;

#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let autd = Controller::builder([AUTD3::new(Point3::origin())])
   .open(autd3::link::Nop::builder())?;
let tr = &autd[0][0];
for tr in &autd[0] {
    // do something
}
Ok(())
}
#include<chrono>
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
auto autd =
autd3::ControllerBuilder({autd3::AUTD3(autd3::Point3::origin())}).open(autd3::link::Nop::builder());
{
auto tr = autd[0][0];
}
{
for (auto& tr : autd[0]) {
  // do something
}
}
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Utils;
using var autd = Controller.Builder([new AUTD3(Point3.Origin)]).Open(Nop.Builder());
{
  var tr = autd[0][0];
  }
foreach (var tr in autd[0])
{
  // do something
}
from pyautd3 import AUTD3, Controller
from pyautd3.link.audit import Audit

autd = Controller.builder([AUTD3([0.0, 0.0, 0.0])]).open(Audit.builder())
tr = autd[0][0]
for _tr in autd[0]:
    pass

Transducer API

Following methods are available for Transducer.

  • idx: Get the local index of the transducer.
  • position: Get the position of the transducer.
  • rotation: Get the rotation of the transducer. The rotation is represented by a quaternion.
  • x_direction: Get the x direction vector of the transducer.
  • y_direction: Get the y direction vector of the transducer.
  • z_direction: Get the z direction vector of the transducer.
  • wavelength: Get the wavelength of the transducer. You need to pass the sound speed as an argument.
  • wavenumber: Get the wavenumber of the transducer. You need to pass the sound speed as an argument.
use autd3::prelude::*;

#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let autd = Controller::builder([AUTD3::new(Point3::origin())])
   .open(autd3::link::Nop::builder())?;
let tr = &autd[0][0];
let idx = tr.idx();
let dev_idx = tr.dev_idx();
let position = tr.position();
Ok(())
}
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
auto autd =
autd3::ControllerBuilder({autd3::AUTD3(autd3::Point3::origin())}).open(autd3::link::Nop::builder());
const auto tr = autd[0][0];
const auto idx = tr.idx();
const auto dev_idx = tr.dev_idx();
const auto position = tr.position();
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Utils;
using var autd = Controller.Builder([new AUTD3(Point3.Origin)]).Open(Nop.Builder());
var tr = autd[0][0];
var trIdx = tr.Idx;
var devIdx = tr.DevIdx;
var position = tr.Position;
from pyautd3 import AUTD3, Controller
from pyautd3.link.audit import Audit

autd = Controller.builder([AUTD3([0.0, 0.0, 0.0])]).open(Audit.builder())
tr = autd[0][0]
idx = tr.idx
dev_idx = tr.dev_idx
position = tr.position