Skip to content

Commit 9104d4c

Browse files
committed
Add 2nd exercise on measure video acquisition latency
1 parent 6009ad6 commit 9104d4c

5 files changed

Lines changed: 353 additions & 8 deletions

tutorials/hobgoblin-closeloop.md

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The exercises below will help you become familiar with using the [Harp Hobgoblin
44

55
## Prerequisites
66

7-
- Install the `Bonsai.Dsp` package from the [Bonsai package manager](https://bonsai-rx.org/docs/articles/packages.html).
7+
- Install the `Bonsai.Dsp`, `Bonsai.Video` and `Bonsai.Vision` packages from the [Bonsai package manager](https://bonsai-rx.org/docs/articles/packages.html).
88

99
## Close-loop latency
1010
In a closed-loop experiment, we want the behaviour data to generate feedback in real-time into the external world, establishing a relationship where the output of the system depends on detected sensory input. Many behavioural experiments in neuroscience require some kind of closed-loop interaction between the subject and the experimental setup.
@@ -19,10 +19,6 @@ Before beginning, set up the `Hobgoblin` with the following `device pattern` tha
1919

2020
- Set the `DumpRegisters` property in the `Hobgoblin` [`Device`] operator to `False`. This is to avoid triggering the command loop in the next exercise.
2121

22-
![Hobgoblin LED](../images/hobgoblin-acquisition-led.svg){width=400px}
23-
24-
- Connect one of the LED modules to digital output channel `GP15` on the `Hobgoblin`.
25-
2622
### Exercise 1: Measuring serial port communication latency
2723

2824
We will take advantage of the device's ability to echo back a timestamped message upon command execution to create a simple loop where each echo re-triggers the same command (toggling the digital output channel `HIGH` and `LOW`). The time interval between the echoes will give us the total closed-loop latency of the system, also known as the round-trip time.
@@ -41,27 +37,76 @@ In order to measure the difference between the timestamps:
4137
- Right-click on the [`Parse`] operator, select the `Output (Bonsai.Harp.Timestamped<Harp.Hobgoblin.DigitalOutputs>)` > `Seconds`.
4238
- Disconnect the `Seconds` node from the [`CreateMessage`] operator.
4339
- Reconnect the [`Parse`] and [`CreateMessage`] operator.
44-
- After the `Seconds` node, insert a [`Difference`] operator from the `Bonsai.Dsp` package.
40+
- After the `Seconds` node, insert a [`Difference`] operator.
4541

46-
Lastly, we will use this sequence to initialize the command loop:
42+
Lastly, we will use this sequence to toggle the digital output and initialize the command loop:
4743

4844
- Insert a [`KeyDown`] operator and set the `Filter` property to the key `A`.
4945
- Insert a [`Parse`] operator and select [`DigitalOutputTogglePayload`] from the `Register` property dropdown menu. Set the `DigitalOutputToggle` property to `GP15`
5046
- Insert a [`MulticastSubject`] operator and configure the `Name` property to `Hobgoblin Commands`.
5147
- Run the workflow, and open the visualizer for the [`Difference`] operator.
5248
**What do you observe?**
5349

50+
### Exercise 2: Measuring video acquisition latency
51+
52+
![Hobgoblin LED](../images/hobgoblin-acquisition-led.svg){width=400px}
53+
- Connect a blue LED module to digital output channel `GP15` on the `Hobgoblin`.
54+
55+
:::workflow
56+
![Hobgoblin Closed-Loop Latency Video](../workflows/hobgoblin-closeloop-latency-video.bonsai)
57+
:::
58+
59+
- Insert a [`VideoCaptureDevice`] operator.
60+
- Insert a [`Crop`] transform.
61+
- Run the workflow and set the `RegionOfInterest` property to a small area around the LED.
62+
63+
> [!Tip]
64+
> You can use the visual editor for an easier calibration. While the workflow is running, right-click on the [`Crop`] transform and select `Show Default Editor` from the context menu or click in the small button with ellipsis that appears when you select the `RegionOfInterest` property.
65+
66+
- Insert a [`Sum`] transform and select the `Val0` field from the output.
67+
68+
> [!Note]
69+
> The [`Sum`] operator adds the value of all the pixels in the image together, across all the color channels. Assuming the default BGR format, the result of summing all the pixels in the `Blue` channel of the image will be stored in `Val0`. `Val1` and `Val2` would store the `Green` and `Red` values, respectively. If you are using an LED with a color other than blue, please select the output field accordingly.
70+
71+
- Insert a [`GreaterThan`] transform.
72+
- Insert a [`BitwiseNot`] transform.
73+
- Insert a [`CreateMessage`] operator, select [`DigitalOutputTogglePayload`] for the `Payload`, and `GP15` for the [`DigitalOutputToggle`] property.
74+
- Insert a [`MulticastSubject`] operator and configure the `Name` property to `Hobgoblin Commands`.
75+
- Run the workflow and use the visualizer of the [`Sum`] operator to choose an appropriate threshold for [`GreaterThan`]. You can use the [`KeyDown`] toggle snippet from the previous exercise to manually toggle the LED.
76+
- Insert a [`DistinctUntilChanged`] operator after the [`BitwiseNot`] transform.
77+
78+
> [!Note]
79+
> The [`DistinctUntilChanged`] operator filters consecutive duplicate items from an observable sequence. In this case, we want to change the value of the LED only when the threshold output changes from `LOW` to `HIGH`, or vice-versa. This will let us measure correctly the latency between detecting a change in the input and measuring the response to that change.
80+
81+
In order to measure the round-trip time between the LED toggle:
82+
83+
:::workflow
84+
![Hobgoblin Closed-Loop Latency Video Measurement](../workflows/hobgoblin-closeloop-latency-video-measurement.bonsai)
85+
:::
86+
87+
- Insert a [`SubscribeSubject`] operator and configure the `Name` property to `Hobgoblin Events`.
88+
- Insert a [`Parse`] operator and select [`TimestampedDigitalOutputTogglePayload`] from the `Register` property dropdown menu.
89+
- Right-click on the [`Parse`] operator, select `Output (Bonsai.Harp.Timestamped<Harp.Hobgoblin.DigitalOutputs>)` > `Seconds` from the context menu.
90+
- Insert a [`Difference`] operator.
91+
- Run the workflow and open the visualizer for the [`Difference`] operator.
92+
93+
_Given the measurements obtained in Exercise 2, what would you estimate is the **input** latency for video acquisition?_
94+
5495
<!--Reference Style Links -->
96+
[`BitwiseNot`]: xref:Bonsai.Expressions.BitwiseNotBuilder
5597
[`CreateMessage`]: xref:Harp.Hobgoblin.CreateMessage
98+
[`Crop`]: xref:Bonsai.Vision.Crop
5699
[`Device`]: xref:Harp.Hobgoblin.Device
57100
[`Difference`]: xref:Bonsai.Dsp.Difference
58101
[`DigitalOutputToggle`]: xref:Harp.Hobgoblin.DigitalOutputToggle
59102
[`DigitalOutputTogglePayload`]: xref:Harp.Hobgoblin.CreateDigitalOutputTogglePayload
103+
[`DistinctUntilChanged`]: xref:Bonsai.Reactive.DistinctUntilChanged
60104
<!-- [`DeviceDataWriter`]: xref:Harp.Hobgoblin.DeviceDataWriter -->
61105
<!-- [`DigitalOutputSet`]: xref:Harp.Hobgoblin.DigitalOutputSet -->
62106
<!-- [`DigitalOutputClear`]: xref:Harp.Hobgoblin.DigitalOutputClear -->
63107
<!-- [`DigitalOutputClearPayload`]: xref:Harp.Hobgoblin.CreateDigitalOutputSetPayload -->
64108
<!-- [`DigitalOutputSetPayload`]: xref:Harp.Hobgoblin.CreateDigitalOutputClearPayload -->
109+
[`GreaterThan`]: xref:Bonsai.Expressions.GreaterThanBuilder
65110
<!-- [`HarpMessage`]: xref:Bonsai.Harp.HarpMessage -->
66111
[`KeyDown`]: xref:Bonsai.Windows.Input.KeyDown
67112
<!-- [`Merge`]: xref:Bonsai.Reactive.Merge -->
@@ -70,8 +115,9 @@ Lastly, we will use this sequence to initialize the command loop:
70115
<!-- [`PublishSubject`]: xref:Bonsai.Reactive.PublishSubject -->
71116
<!-- [`RollingGraph`]: xref:Bonsai.Gui.ZedGraph.RollingGraphBuilder -->
72117
[`SubscribeSubject`]: xref:Bonsai.Expressions.SubscribeSubject
73-
<!-- [`TimestampedAnalogData`]: xref:Harp.Hobgoblin.TimestampedAnalogData -->
118+
[`Sum`]: xref:Bonsai.Dsp.Sum
74119
[`TimestampedDigitalOutputTogglePayload`]: xref:Harp.Hobgoblin.TimestampedDigitalOutputToggle
75120
<!-- [`TimestampedDigitalOutputSet`]: xref:Harp.Hobgoblin.TimestampedDigitalOutputSet -->
76121
<!-- [`TimestampedDigitalOutputClear`]: xref:Harp.Hobgoblin.TimestampedDigitalOutputClear -->
122+
[`VideoCaptureDevice`]: xref:Bonsai.Video.VideoCaptureDevice
77123
<!-- [`Zip`]: xref:Bonsai.Reactive.Zip -->
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<WorkflowBuilder Version="2.9.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:p1="clr-namespace:Harp.Hobgoblin;assembly=Harp.Hobgoblin"
5+
xmlns:harp="clr-namespace:Bonsai.Harp;assembly=Bonsai.Harp"
6+
xmlns:dsp="clr-namespace:Bonsai.Dsp;assembly=Bonsai.Dsp"
7+
xmlns="https://bonsai-rx.org/2018/workflow">
8+
<Workflow>
9+
<Nodes>
10+
<Expression xsi:type="SubscribeSubject">
11+
<Name>Hobgoblin Events</Name>
12+
</Expression>
13+
<Expression xsi:type="p1:Parse">
14+
<harp:Register xsi:type="p1:TimestampedDigitalOutputToggle" />
15+
</Expression>
16+
<Expression xsi:type="MemberSelector">
17+
<Selector>Seconds</Selector>
18+
</Expression>
19+
<Expression xsi:type="Combinator">
20+
<Combinator xsi:type="dsp:Difference">
21+
<dsp:Order>1</dsp:Order>
22+
</Combinator>
23+
</Expression>
24+
</Nodes>
25+
<Edges>
26+
<Edge From="0" To="1" Label="Source1" />
27+
<Edge From="1" To="2" Label="Source1" />
28+
<Edge From="2" To="3" Label="Source1" />
29+
</Edges>
30+
</Workflow>
31+
</WorkflowBuilder>
Lines changed: 69 additions & 0 deletions
Loading
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<WorkflowBuilder Version="2.8.5"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:vid="clr-namespace:Bonsai.Video;assembly=Bonsai.Video"
5+
xmlns:cv="clr-namespace:Bonsai.Vision;assembly=Bonsai.Vision"
6+
xmlns:dsp="clr-namespace:Bonsai.Dsp;assembly=Bonsai.Dsp"
7+
xmlns:rx="clr-namespace:Bonsai.Reactive;assembly=Bonsai.Core"
8+
xmlns:p1="clr-namespace:Harp.Hobgoblin;assembly=Harp.Hobgoblin"
9+
xmlns:harp="clr-namespace:Bonsai.Harp;assembly=Bonsai.Harp"
10+
xmlns="https://bonsai-rx.org/2018/workflow">
11+
<Workflow>
12+
<Nodes>
13+
<Expression xsi:type="Combinator">
14+
<Combinator xsi:type="vid:VideoCaptureDevice">
15+
<vid:Index>2</vid:Index>
16+
<vid:CaptureProperties />
17+
</Combinator>
18+
</Expression>
19+
<Expression xsi:type="Combinator">
20+
<Combinator xsi:type="cv:Crop">
21+
<cv:RegionOfInterest>
22+
<cv:X>0</cv:X>
23+
<cv:Y>0</cv:Y>
24+
<cv:Width>0</cv:Width>
25+
<cv:Height>0</cv:Height>
26+
</cv:RegionOfInterest>
27+
</Combinator>
28+
</Expression>
29+
<Expression xsi:type="Combinator">
30+
<Combinator xsi:type="dsp:Sum" />
31+
</Expression>
32+
<Expression xsi:type="MemberSelector">
33+
<Selector>Val0</Selector>
34+
</Expression>
35+
<Expression xsi:type="GreaterThan">
36+
<Operand xsi:type="DoubleProperty">
37+
<Value>0</Value>
38+
</Operand>
39+
</Expression>
40+
<Expression xsi:type="BitwiseNot" />
41+
<Expression xsi:type="Combinator">
42+
<Combinator xsi:type="rx:DistinctUntilChanged" />
43+
</Expression>
44+
<Expression xsi:type="p1:CreateMessage">
45+
<harp:MessageType>Write</harp:MessageType>
46+
<harp:Payload xsi:type="p1:CreateDigitalOutputTogglePayload">
47+
<p1:DigitalOutputToggle>GP15</p1:DigitalOutputToggle>
48+
</harp:Payload>
49+
</Expression>
50+
<Expression xsi:type="MulticastSubject">
51+
<Name>Hobgoblin Commands</Name>
52+
</Expression>
53+
</Nodes>
54+
<Edges>
55+
<Edge From="0" To="1" Label="Source1" />
56+
<Edge From="1" To="2" Label="Source1" />
57+
<Edge From="2" To="3" Label="Source1" />
58+
<Edge From="3" To="4" Label="Source1" />
59+
<Edge From="4" To="5" Label="Source1" />
60+
<Edge From="5" To="6" Label="Source1" />
61+
<Edge From="6" To="7" Label="Source1" />
62+
<Edge From="7" To="8" Label="Source1" />
63+
</Edges>
64+
</Workflow>
65+
</WorkflowBuilder>

0 commit comments

Comments
 (0)