diff --git a/examples/rig.py b/examples/rig.py
index 4216c5d..67b3237 100644
--- a/examples/rig.py
+++ b/examples/rig.py
@@ -2,7 +2,6 @@
from aind_behavior_services.rig import cameras
from aind_behavior_services.rig.aind_manipulator import (
- AindManipulator,
AindManipulatorCalibration,
Axis,
AxisConfiguration,
@@ -15,7 +14,12 @@
)
from aind_behavior_services.rig.water_valve import Measurement, calibrate_water_valves
-from aind_behavior_dynamic_foraging.rig import AindDynamicForagingRig, DynamicForagingSoundCard, RigCalibration
+from aind_behavior_dynamic_foraging.rig import (
+ AindDynamicForagingRig,
+ DynamicForagingAindManipulator,
+ DynamicForagingSoundCard,
+ RigCalibration,
+)
manipulator_calibration = AindManipulatorCalibration(
full_step_to_mm=(ManipulatorPosition(x=0.010, y1=0.010, y2=0.010, z=0.010)),
@@ -60,7 +64,7 @@
harp_lickometer_left=None,
harp_lickometer_right=None,
harp_clock_generator=HarpWhiteRabbit(port_name="COM11"),
- manipulator=AindManipulator(port_name="COM9", calibration=manipulator_calibration),
+ manipulator=DynamicForagingAindManipulator(port_name="COM9", calibration=manipulator_calibration),
calibration=RigCalibration(
water_valve_left=water_valve_calibration,
water_valve_right=water_valve_calibration,
diff --git a/schema/aind_behavior_dynamic_foraging.json b/schema/aind_behavior_dynamic_foraging.json
index 2107795..4b61a1b 100644
--- a/schema/aind_behavior_dynamic_foraging.json
+++ b/schema/aind_behavior_dynamic_foraging.json
@@ -107,7 +107,7 @@
]
},
"manipulator": {
- "$ref": "#/$defs/AindManipulator",
+ "$ref": "#/$defs/DynamicForagingAindManipulator",
"description": "Manipulator"
},
"calibration": {
@@ -218,114 +218,6 @@
"title": "AindDynamicForagingTaskParameters",
"type": "object"
},
- "AindManipulator": {
- "description": "AindManipulator device definition",
- "properties": {
- "device_type": {
- "const": "StepperDriver",
- "default": "StepperDriver",
- "title": "Device Type",
- "type": "string"
- },
- "calibration": {
- "$ref": "#/$defs/AindManipulatorCalibration",
- "default": {
- "description": "AindManipulator calibration and settings",
- "full_step_to_mm": {
- "x": 0.01,
- "y1": 0.01,
- "y2": 0.01,
- "z": 0.01
- },
- "axis_configuration": [
- {
- "axis": 2,
- "max_limit": 25.0,
- "maximum_step_interval": 2000,
- "microstep_resolution": 0,
- "min_limit": -0.01,
- "motor_operation_mode": 0,
- "step_acceleration_interval": 100,
- "step_interval": 100
- },
- {
- "axis": 3,
- "max_limit": 25.0,
- "maximum_step_interval": 2000,
- "microstep_resolution": 0,
- "min_limit": -0.01,
- "motor_operation_mode": 0,
- "step_acceleration_interval": 100,
- "step_interval": 100
- },
- {
- "axis": 1,
- "max_limit": 25.0,
- "maximum_step_interval": 2000,
- "microstep_resolution": 0,
- "min_limit": -0.01,
- "motor_operation_mode": 0,
- "step_acceleration_interval": 100,
- "step_interval": 100
- },
- {
- "axis": 4,
- "max_limit": 25.0,
- "maximum_step_interval": 2000,
- "microstep_resolution": 0,
- "min_limit": -0.01,
- "motor_operation_mode": 0,
- "step_acceleration_interval": 100,
- "step_interval": 100
- }
- ],
- "homing_order": [
- 2,
- 3,
- 1,
- 4
- ],
- "initial_position": {
- "x": 0.0,
- "y1": 0.0,
- "y2": 0.0,
- "z": 0.0
- }
- },
- "description": "Calibration for the device."
- },
- "who_am_i": {
- "const": 1130,
- "default": 1130,
- "title": "Who Am I",
- "type": "integer"
- },
- "serial_number": {
- "default": null,
- "description": "Device serial number",
- "oneOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Serial Number"
- },
- "port_name": {
- "description": "Device port name",
- "title": "Port Name",
- "type": "string"
- }
- },
- "required": [
- "port_name"
- ],
- "title": "AindManipulator",
- "type": "object",
- "x-sgen-typename": "AllenNeuralDynamics.AindManipulator.AindManipulator"
- },
"AindManipulatorCalibration": {
"description": "AindManipulator calibration class",
"properties": {
@@ -1470,6 +1362,123 @@
"title": "Distribution",
"x-sgen-typename": "AllenNeuralDynamics.AindBehaviorServices.Distributions.Distribution"
},
+ "DynamicForagingAindManipulator": {
+ "description": "A calibrated manipulator for the dynamic foraging rig. This is a subclass of the AindManipulator that includes an offset of the subject position relative to the initial position.",
+ "properties": {
+ "device_type": {
+ "const": "StepperDriver",
+ "default": "StepperDriver",
+ "title": "Device Type",
+ "type": "string"
+ },
+ "calibration": {
+ "$ref": "#/$defs/AindManipulatorCalibration",
+ "default": {
+ "description": "AindManipulator calibration and settings",
+ "full_step_to_mm": {
+ "x": 0.01,
+ "y1": 0.01,
+ "y2": 0.01,
+ "z": 0.01
+ },
+ "axis_configuration": [
+ {
+ "axis": 2,
+ "max_limit": 25.0,
+ "maximum_step_interval": 2000,
+ "microstep_resolution": 0,
+ "min_limit": -0.01,
+ "motor_operation_mode": 0,
+ "step_acceleration_interval": 100,
+ "step_interval": 100
+ },
+ {
+ "axis": 3,
+ "max_limit": 25.0,
+ "maximum_step_interval": 2000,
+ "microstep_resolution": 0,
+ "min_limit": -0.01,
+ "motor_operation_mode": 0,
+ "step_acceleration_interval": 100,
+ "step_interval": 100
+ },
+ {
+ "axis": 1,
+ "max_limit": 25.0,
+ "maximum_step_interval": 2000,
+ "microstep_resolution": 0,
+ "min_limit": -0.01,
+ "motor_operation_mode": 0,
+ "step_acceleration_interval": 100,
+ "step_interval": 100
+ },
+ {
+ "axis": 4,
+ "max_limit": 25.0,
+ "maximum_step_interval": 2000,
+ "microstep_resolution": 0,
+ "min_limit": -0.01,
+ "motor_operation_mode": 0,
+ "step_acceleration_interval": 100,
+ "step_interval": 100
+ }
+ ],
+ "homing_order": [
+ 2,
+ 3,
+ 1,
+ 4
+ ],
+ "initial_position": {
+ "x": 0.0,
+ "y1": 0.0,
+ "y2": 0.0,
+ "z": 0.0
+ }
+ },
+ "description": "Calibration for the device."
+ },
+ "who_am_i": {
+ "const": 1130,
+ "default": 1130,
+ "title": "Who Am I",
+ "type": "integer"
+ },
+ "serial_number": {
+ "default": null,
+ "description": "Device serial number",
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Serial Number"
+ },
+ "port_name": {
+ "description": "Device port name",
+ "title": "Port Name",
+ "type": "string"
+ },
+ "subject_offset": {
+ "$ref": "#/$defs/ManipulatorPosition",
+ "default": {
+ "x": 0.0,
+ "y1": 0.0,
+ "y2": 0.0,
+ "z": 0.0
+ },
+ "description": "Offset of the subject position relative to the `initial_position` parameter"
+ }
+ },
+ "required": [
+ "port_name"
+ ],
+ "title": "DynamicForagingAindManipulator",
+ "type": "object"
+ },
"DynamicForagingSoundCard": {
"description": "A calibrated sound card for the dynamic foraging rig. This is a subclass of the HarpSoundCard that includes the sound card calibration.",
"properties": {
diff --git a/src/Extensions/AindBehaviorDynamicForaging.Generated.cs b/src/Extensions/AindBehaviorDynamicForaging.Generated.cs
index 0479d06..9810505 100644
--- a/src/Extensions/AindBehaviorDynamicForaging.Generated.cs
+++ b/src/Extensions/AindBehaviorDynamicForaging.Generated.cs
@@ -43,7 +43,7 @@ public partial class AindDynamicForagingRig
private HarpEnvironmentSensor _harpEnvironmentSensor;
- private AllenNeuralDynamics.AindManipulator.AindManipulator _manipulator;
+ private DynamicForagingAindManipulator _manipulator;
private RigCalibration _calibration;
@@ -55,7 +55,7 @@ public AindDynamicForagingRig()
_harpBehavior = new HarpBehavior();
_harpClockGenerator = new HarpWhiteRabbit();
_harpSoundCard = new DynamicForagingSoundCard();
- _manipulator = new AllenNeuralDynamics.AindManipulator.AindManipulator();
+ _manipulator = new DynamicForagingAindManipulator();
_calibration = new RigCalibration();
}
@@ -324,7 +324,7 @@ public HarpEnvironmentSensor HarpEnvironmentSensor
[System.Xml.Serialization.XmlIgnoreAttribute()]
[Newtonsoft.Json.JsonPropertyAttribute("manipulator", Required=Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DescriptionAttribute("Manipulator")]
- public AllenNeuralDynamics.AindManipulator.AindManipulator Manipulator
+ public DynamicForagingAindManipulator Manipulator
{
get
{
@@ -2661,6 +2661,180 @@ protected override bool PrintMembers(System.Text.StringBuilder stringBuilder)
}
+ ///
+ /// A calibrated manipulator for the dynamic foraging rig. This is a subclass of the AindManipulator that includes an offset of the subject position relative to the initial position.
+ ///
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.9.0.0 (Newtonsoft.Json v13.0.0.0)")]
+ [System.ComponentModel.DescriptionAttribute("A calibrated manipulator for the dynamic foraging rig. This is a subclass of the " +
+ "AindManipulator that includes an offset of the subject position relative to the " +
+ "initial position.")]
+ [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)]
+ [Bonsai.CombinatorAttribute(MethodName="Generate")]
+ public partial class DynamicForagingAindManipulator
+ {
+
+ private string _deviceType;
+
+ private AllenNeuralDynamics.AindManipulator.AindManipulatorCalibration _calibration;
+
+ private int _whoAmI;
+
+ private string _serialNumber;
+
+ private string _portName;
+
+ private AllenNeuralDynamics.AindManipulator.ManipulatorPosition _subjectOffset;
+
+ public DynamicForagingAindManipulator()
+ {
+ _deviceType = "StepperDriver";
+ _calibration = new AllenNeuralDynamics.AindManipulator.AindManipulatorCalibration();
+ _whoAmI = 1130;
+ _subjectOffset = new AllenNeuralDynamics.AindManipulator.ManipulatorPosition();
+ }
+
+ protected DynamicForagingAindManipulator(DynamicForagingAindManipulator other)
+ {
+ _deviceType = other._deviceType;
+ _calibration = other._calibration;
+ _whoAmI = other._whoAmI;
+ _serialNumber = other._serialNumber;
+ _portName = other._portName;
+ _subjectOffset = other._subjectOffset;
+ }
+
+ [Newtonsoft.Json.JsonPropertyAttribute("device_type")]
+ public string DeviceType
+ {
+ get
+ {
+ return _deviceType;
+ }
+ set
+ {
+ _deviceType = value;
+ }
+ }
+
+ ///
+ /// Calibration for the device.
+ ///
+ [System.Xml.Serialization.XmlIgnoreAttribute()]
+ [Newtonsoft.Json.JsonPropertyAttribute("calibration")]
+ [System.ComponentModel.DescriptionAttribute("Calibration for the device.")]
+ public AllenNeuralDynamics.AindManipulator.AindManipulatorCalibration Calibration
+ {
+ get
+ {
+ return _calibration;
+ }
+ set
+ {
+ _calibration = value;
+ }
+ }
+
+ [Newtonsoft.Json.JsonPropertyAttribute("who_am_i")]
+ public int WhoAmI
+ {
+ get
+ {
+ return _whoAmI;
+ }
+ set
+ {
+ _whoAmI = value;
+ }
+ }
+
+ ///
+ /// Device serial number
+ ///
+ [Newtonsoft.Json.JsonPropertyAttribute("serial_number")]
+ [System.ComponentModel.DescriptionAttribute("Device serial number")]
+ public string SerialNumber
+ {
+ get
+ {
+ return _serialNumber;
+ }
+ set
+ {
+ _serialNumber = value;
+ }
+ }
+
+ ///
+ /// Device port name
+ ///
+ [Newtonsoft.Json.JsonPropertyAttribute("port_name", Required=Newtonsoft.Json.Required.Always)]
+ [System.ComponentModel.DescriptionAttribute("Device port name")]
+ public string PortName
+ {
+ get
+ {
+ return _portName;
+ }
+ set
+ {
+ _portName = value;
+ }
+ }
+
+ ///
+ /// Offset of the subject position relative to the `initial_position` parameter
+ ///
+ [System.Xml.Serialization.XmlIgnoreAttribute()]
+ [Newtonsoft.Json.JsonPropertyAttribute("subject_offset")]
+ [System.ComponentModel.DescriptionAttribute("Offset of the subject position relative to the `initial_position` parameter")]
+ public AllenNeuralDynamics.AindManipulator.ManipulatorPosition SubjectOffset
+ {
+ get
+ {
+ return _subjectOffset;
+ }
+ set
+ {
+ _subjectOffset = value;
+ }
+ }
+
+ public System.IObservable Generate()
+ {
+ return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new DynamicForagingAindManipulator(this)));
+ }
+
+ public System.IObservable Generate(System.IObservable source)
+ {
+ return System.Reactive.Linq.Observable.Select(source, _ => new DynamicForagingAindManipulator(this));
+ }
+
+ protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder)
+ {
+ stringBuilder.Append("DeviceType = " + _deviceType + ", ");
+ stringBuilder.Append("Calibration = " + _calibration + ", ");
+ stringBuilder.Append("WhoAmI = " + _whoAmI + ", ");
+ stringBuilder.Append("SerialNumber = " + _serialNumber + ", ");
+ stringBuilder.Append("PortName = " + _portName + ", ");
+ stringBuilder.Append("SubjectOffset = " + _subjectOffset);
+ return true;
+ }
+
+ public override string ToString()
+ {
+ System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
+ stringBuilder.Append(GetType().Name);
+ stringBuilder.Append(" { ");
+ if (PrintMembers(stringBuilder))
+ {
+ stringBuilder.Append(" ");
+ }
+ stringBuilder.Append("}");
+ return stringBuilder.ToString();
+ }
+ }
+
+
///
/// A calibrated sound card for the dynamic foraging rig. This is a subclass of the HarpSoundCard that includes the sound card calibration.
///
@@ -7634,6 +7808,11 @@ public System.IObservable Process(System.IObservable(source);
}
+ public System.IObservable Process(System.IObservable source)
+ {
+ return Process(source);
+ }
+
public System.IObservable Process(System.IObservable source)
{
return Process(source);
@@ -7804,6 +7983,7 @@ public System.IObservable Process(System.IObservable source)
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))]
+ [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))]
diff --git a/src/Extensions/Hardware.bonsai b/src/Extensions/Hardware.bonsai
index 83ea506..c1730e2 100644
--- a/src/Extensions/Hardware.bonsai
+++ b/src/Extensions/Hardware.bonsai
@@ -4,11 +4,12 @@
xmlns:harp="clr-namespace:Bonsai.Harp;assembly=Bonsai.Harp"
xmlns:p1="clr-namespace:AllenNeuralDynamics.WhiteRabbit;assembly=AllenNeuralDynamics.WhiteRabbit"
xmlns:rx="clr-namespace:Bonsai.Reactive;assembly=Bonsai.Core"
+ xmlns:p2="clr-namespace:;assembly=Extensions"
xmlns:beh="clr-namespace:Harp.Behavior;assembly=Harp.Behavior"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:cv="clr-namespace:Bonsai.Vision;assembly=Bonsai.Vision"
- xmlns:p2="clr-namespace:OpenCV.Net;assembly=OpenCV.Net"
- xmlns:p3="clr-namespace:AllenNeuralDynamics.Core;assembly=AllenNeuralDynamics.Core"
+ xmlns:p3="clr-namespace:OpenCV.Net;assembly=OpenCV.Net"
+ xmlns:p4="clr-namespace:AllenNeuralDynamics.Core;assembly=AllenNeuralDynamics.Core"
xmlns:spk="clr-namespace:Bonsai.Spinnaker;assembly=Bonsai.Spinnaker"
xmlns="https://bonsai-rx.org/2018/workflow">
@@ -132,7 +133,10 @@
RigSchema
- Manipulator.Calibration
+ Manipulator
+
+
+
RigSchema
@@ -228,24 +232,25 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
+
@@ -399,7 +404,7 @@
-
+
RegionOfInterest
0
@@ -414,21 +419,21 @@
-
+
Default
- 19000
- 0
- 1
-
- Mono8
-
- 0
- 0
- 0
- 0
-
-
+ 19000
+ 0
+ 1
+
+ Mono8
+
+ 0
+ 0
+ 0
+ 0
+
+
diff --git a/src/Extensions/Logging.bonsai b/src/Extensions/Logging.bonsai
index 26f8a32..fffea67 100644
--- a/src/Extensions/Logging.bonsai
+++ b/src/Extensions/Logging.bonsai
@@ -775,22 +775,6 @@
AdditionalSoftwareEvents
-
- ManipulatorPosition
-
-
-
- 1
-
-
-
-
- InitialManipulatorPosition
-
-
-
- SoftwareEvent
-
RngSeedValue
@@ -822,12 +806,9 @@
-
+
-
-
-
diff --git a/src/Extensions/OffsetInitialPositionWithSubject.cs b/src/Extensions/OffsetInitialPositionWithSubject.cs
new file mode 100644
index 0000000..e79f0f0
--- /dev/null
+++ b/src/Extensions/OffsetInitialPositionWithSubject.cs
@@ -0,0 +1,30 @@
+using Bonsai;
+using System;
+using System.ComponentModel;
+using System.Linq;
+using System.Reactive.Linq;
+using AindDynamicForagingDataSchema;
+using AllenNeuralDynamics.AindManipulator;
+using Newtonsoft.Json;
+
+[Combinator]
+[Description("Offsets the initial position of the manipulator with the subject's calibration.")]
+[WorkflowElementCategory(ElementCategory.Transform)]
+public class OffsetInitialPositionWithSubject
+{
+ public IObservable Process(IObservable source)
+ {
+ return source.Select(value =>
+ {
+ // Make a quick deep-copy just in case.
+ var calibration = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value.Calibration));
+ if (calibration == null)
+ {
+ throw new InvalidOperationException("Manipulator does not contain a valid initial position.");
+ }
+ calibration.InitialPosition = calibration.InitialPosition + value.SubjectOffset;
+ return calibration;
+ }
+ );
+ }
+}
diff --git a/src/Extensions/OperationControl.bonsai b/src/Extensions/OperationControl.bonsai
index 9735b8a..7931b7d 100644
--- a/src/Extensions/OperationControl.bonsai
+++ b/src/Extensions/OperationControl.bonsai
@@ -632,7 +632,7 @@
- Manipulator
+ ManipulatorTask
@@ -882,6 +882,117 @@
+
+ ManipulatorInitialization
+
+
+
+
+ 0
+
+
+
+
+ PT0.5S
+
+
+
+
+ This is a publish subject!
+
+
+
+ HomeMotors
+
+
+ WaitForHomed
+
+
+
+ IsHomed
+
+
+
+ PT0.2S
+
+
+
+
+
+
+ Source1
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PT10S
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GoToInit
+
+
+
+ Source1
+
+
+
+ GoToInitialPosition
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CameraControl
@@ -1516,8 +1627,8 @@
-
-
+
+
\ No newline at end of file
diff --git a/src/aind_behavior_dynamic_foraging/rig.py b/src/aind_behavior_dynamic_foraging/rig.py
index c1a94f0..8608a95 100644
--- a/src/aind_behavior_dynamic_foraging/rig.py
+++ b/src/aind_behavior_dynamic_foraging/rig.py
@@ -62,6 +62,16 @@ class DynamicForagingSoundCard(harp.HarpSoundCard):
)
+class DynamicForagingAindManipulator(aind_manipulator.AindManipulator):
+ """A calibrated manipulator for the dynamic foraging rig. This is a subclass of the AindManipulator that includes an offset of the subject position relative to the initial position."""
+
+ subject_offset: aind_manipulator.ManipulatorPosition = Field(
+ aind_manipulator.ManipulatorPosition(x=0, y1=0, y2=0, z=0),
+ description="Offset of the subject position relative to the `initial_position` parameter",
+ validate_default=True,
+ )
+
+
class AindDynamicForagingRig(rig.Rig):
version: Literal[__semver__] = __semver__
triggered_camera_controller: cameras.CameraController[cameras.SpinnakerCamera] = Field(
@@ -79,5 +89,5 @@ class AindDynamicForagingRig(rig.Rig):
harp_environment_sensor: Optional[harp.HarpEnvironmentSensor] = Field(
default=None, description="Harp environment sensor"
)
- manipulator: aind_manipulator.AindManipulator = Field(description="Manipulator")
+ manipulator: DynamicForagingAindManipulator = Field(description="Manipulator")
calibration: RigCalibration = Field(description="Calibration models")