Lay Out a Transducer Array#
An OmniAcoustic prim can represent a transducer array. Mounts locate the
transducers, receiver groups select which mounts listen, and firing sequences
schedule transmissions with channel ids. This procedure expands the sensor
from Set Up an Acoustic Sensor and checks the resulting SignalWays layout.
Applying OmniSensorGenericAcousticWpmAPI supplies mount m001, receiver
group g001 and firing sequence seq001. Override these instances and add
more as needed. See the default topology for their values.
Before you start. Start with the working one-transducer sensor from Set Up an Acoustic Sensor. See topology attributes for the three schema groups.
Add mounts#
Keep both the mount count and distinct channel count at or below 255, and keep their product (the number of signal ways) at or below 255. These limits are not enforced by parameter validation.
Prepend OmniSensorWpmAcousticSensorMountAPI:m002 to the prim’s
apiSchemas, then m003 and upwards, one instance per transducer, and
author each instance’s position and rotation under its own instance
name, omni:sensor:WpmAcoustic:sensorMount:m002:position and
omni:sensor:WpmAcoustic:sensorMount:m002:rotation; m001 is already
applied, so you override its two attributes directly. The units, the sensor
frame both values are relative to and the sense of each rotation are on
the mount frame, and
the shape of the names, with their group and instance segments, on
namespaces.
Group the receivers#
Add OmniSensorWpmAcousticRxGroupAPI:g002 for another receiver group and
set receiverIndices to its mount indices. [0] selects m001;
[0, 1] selects m001 and m002. Each event selects one group, and
each receiver that detects an echo records it. Create a group for each required
receiver subset. The default g001 can be overridden directly.
Every index must name a mount that exists, counting from 0 in instance
order, and every group needs a non-empty list (the index requirements).
Write the firing sequence#
The main schema includes seq001. Set its four arrays together:
eventTimeNs, txSensorId, rxGroupId and channel. They must have
one entry per event and equal lengths. Number channel ids
from zero without gaps; use separate ids for events whose echoes must remain
separable. See channel-id requirements and
output slots.
For a second cycle, prepend OmniSensorWpmAcousticFiringSeqAPI:seq002 and
author all four of its arrays; the frames then cycle through the sequences, one
sequence per frame, as Outputs describes under cycleIndex.
Instance ordering#
The indices in receiverIndices, txSensorId and rxGroupId are
positions in the instance names’ lexicographic order, so keep the schema’s
zero-padded m001 form; the ordering rule, including how names are compared,
is on instance order.
Two-mount example#
Two mounts, two receiver groups and a two-event cycle on one prim look like this:
#usda 1.0
def OmniAcoustic "acoustic_array" (
prepend apiSchemas = [
"OmniSensorGenericAcousticWpmAPI",
"OmniSensorWpmAcousticSensorMountAPI:m002",
"OmniSensorWpmAcousticRxGroupAPI:g002"
]
)
{
# m001 is applied by the main schema; this overrides its defaults
float3 omni:sensor:WpmAcoustic:sensorMount:m001:position = (4.0, 0.5, 0.5)
float3 omni:sensor:WpmAcoustic:sensorMount:m001:rotation = (0.0, -1.0, 0.0)
# m002 exists because it was prepended above
float3 omni:sensor:WpmAcoustic:sensorMount:m002:position = (4.0, -0.5, 0.5)
float3 omni:sensor:WpmAcoustic:sensorMount:m002:rotation = (0.0, -1.0, 0.0)
# both groups listen with both mounts
uint[] omni:sensor:WpmAcoustic:rxGroup:g001:receiverIndices = [0, 1]
uint[] omni:sensor:WpmAcoustic:rxGroup:g002:receiverIndices = [0, 1]
# one cycle of two events: m001 fires at 0 into g001 on channel 0,
# m002 fires 5 µs later into g002 on channel 1
float[] omni:sensor:WpmAcoustic:firingSeq:seq001:eventTimeNs = [0.0, 5000.0]
uint[] omni:sensor:WpmAcoustic:firingSeq:seq001:txSensorId = [0, 1]
uint[] omni:sensor:WpmAcoustic:firingSeq:seq001:rxGroupId = [0, 1]
uint[] omni:sensor:WpmAcoustic:firingSeq:seq001:channel = [0, 1]
}
from pxr import Gf, Sdf
acoustic_array = stage.DefinePrim("/acoustic_array", "OmniAcoustic")
acoustic_array.AddAppliedSchema("OmniSensorGenericAcousticWpmAPI")
acoustic_array.AddAppliedSchema("OmniSensorWpmAcousticSensorMountAPI:m002")
acoustic_array.AddAppliedSchema("OmniSensorWpmAcousticRxGroupAPI:g002")
# The main schema supplies m001; override its position and rotation.
acoustic_array.CreateAttribute(
"omni:sensor:WpmAcoustic:sensorMount:m001:position", Sdf.ValueTypeNames.Float3, custom=False
).Set(Gf.Vec3f(4.0, 0.5, 0.5))
acoustic_array.CreateAttribute(
"omni:sensor:WpmAcoustic:sensorMount:m001:rotation", Sdf.ValueTypeNames.Float3, custom=False
).Set(Gf.Vec3f(0.0, -1.0, 0.0))
# m002 is the second mount, applied above.
acoustic_array.CreateAttribute(
"omni:sensor:WpmAcoustic:sensorMount:m002:position", Sdf.ValueTypeNames.Float3, custom=False
).Set(Gf.Vec3f(4.0, -0.5, 0.5))
acoustic_array.CreateAttribute(
"omni:sensor:WpmAcoustic:sensorMount:m002:rotation", Sdf.ValueTypeNames.Float3, custom=False
).Set(Gf.Vec3f(0.0, -1.0, 0.0))
# Both groups listen with both mounts, in sorted mount order.
acoustic_array.CreateAttribute(
"omni:sensor:WpmAcoustic:rxGroup:g001:receiverIndices",
Sdf.ValueTypeNames.UIntArray,
custom=False,
).Set([0, 1])
acoustic_array.CreateAttribute(
"omni:sensor:WpmAcoustic:rxGroup:g002:receiverIndices",
Sdf.ValueTypeNames.UIntArray,
custom=False,
).Set([0, 1])
# Two events: mount 0 fires on channel 0; mount 1 follows 5 microseconds later on channel 1.
acoustic_array.CreateAttribute(
"omni:sensor:WpmAcoustic:firingSeq:seq001:eventTimeNs",
Sdf.ValueTypeNames.FloatArray,
custom=False,
).Set([0.0, 5000.0])
acoustic_array.CreateAttribute(
"omni:sensor:WpmAcoustic:firingSeq:seq001:txSensorId",
Sdf.ValueTypeNames.UIntArray,
custom=False,
).Set([0, 1])
acoustic_array.CreateAttribute(
"omni:sensor:WpmAcoustic:firingSeq:seq001:rxGroupId",
Sdf.ValueTypeNames.UIntArray,
custom=False,
).Set([0, 1])
acoustic_array.CreateAttribute(
"omni:sensor:WpmAcoustic:firingSeq:seq001:channel",
Sdf.ValueTypeNames.UIntArray,
custom=False,
).Set([0, 1])
The two mounts sit 4 m ahead of the prim’s origin and 0.5 m above it, one
0.5 m to the left and one 0.5 m to the right, so 1 m apart, both tilted by one
degree about the lateral axis. Both groups hold [0, 1], so both mounts
listen to every event; two groups are declared so that each event can be given
its own listening subset later, for example [1] on g002 to let m002
listen alone to its own pulse. The cycle fires m001 at time 0 into
g001 on channel 0 and m002 5000 ns later into g002 on channel
1; the two channel ids keep the two pulses apart at each receiver.
The worked example from above: two mounts 1 m apart in the sensor frame,
both groups listening, event 0 from m001 on channel 0, event 1 from
m002 on channel 1, and the four signal ways that result, one per mount
and channel id.#
Verification#
Render a frame and check the SignalWays output:
numSignalWaysreports the number of allocated signal ways. Two mounts and channel ids0and1give4.Count the entries whose
Flagscontain theVALIDbit to find how many signal ways received an echo. In this example, each firing event uses both mounts as receivers, so all four signal ways can be valid with a reflecting surface in front of the array.
See Outputs for the channels and parameters. After changing the layout, compare these counts before and after the edit; Retune the Sensor While It Runs explains the procedure and why the valid count may not increase.
Use Sensor Definition Validation to check the sensor definition before rendering.
Potential issues#
An index selects the wrong mount or group.
receiverIndices,txSensorIdandrxGroupIduse zero-based positions in lexicographically sorted instance names. Use zero-padded names so their lexical and numeric order agree; see instance order.A receiver index produces an error. The index refers to a nonexistent mount, so the model removes it from the receiver group. Correct the index and ensure the group contains at least one receiver; see index requirements.
A group index produces an error and the event uses group 0. The event’s
rxGroupIdrefers to a nonexistent group, so the model substitutes group0. Set it to the index of the intended receiver group; see index requirements.
See the Symptom Index for other sensor issues.