Return notifications about pid and drive updates. Introduce debounce
This commit is contained in:
parent
fe98a836f8
commit
74301afb42
@ -74,6 +74,7 @@ public class VirtualDrive(
|
|||||||
|
|
||||||
// compute new value based on velocity and acceleration from the previous step
|
// compute new value based on velocity and acceleration from the previous step
|
||||||
positionState.value += velocity * dtSeconds + force / mass * dtSeconds.pow(2) / 2
|
positionState.value += velocity * dtSeconds + force / mass * dtSeconds.pow(2) / 2
|
||||||
|
propertyChanged(Drive.position, positionState.value)
|
||||||
|
|
||||||
// compute new velocity based on acceleration on the previous step
|
// compute new velocity based on acceleration on the previous step
|
||||||
velocity += force / mass * dtSeconds
|
velocity += force / mass * dtSeconds
|
||||||
|
@ -62,6 +62,7 @@ public class PidRegulator(
|
|||||||
lastPosition = drive.position
|
lastPosition = drive.position
|
||||||
|
|
||||||
drive.force = pidParameters.kp * delta + pidParameters.ki * integral + pidParameters.kd * derivative
|
drive.force = pidParameters.kp * delta + pidParameters.ki * integral + pidParameters.kd * derivative
|
||||||
|
propertyChanged(Regulator.position, drive.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
|
@file:OptIn(FlowPreview::class)
|
||||||
|
|
||||||
package space.kscience.controls.vision
|
package space.kscience.controls.vision
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.debounce
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.transform
|
import kotlinx.coroutines.flow.transform
|
||||||
@ -25,6 +29,7 @@ import space.kscience.plotly.models.TraceValues
|
|||||||
import space.kscience.plotly.scatter
|
import space.kscience.plotly.scatter
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.hours
|
import kotlin.time.Duration.Companion.hours
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
private var TraceValues.values: List<Value>
|
private var TraceValues.values: List<Value>
|
||||||
get() = value?.list ?: emptyList()
|
get() = value?.list ?: emptyList()
|
||||||
@ -88,12 +93,13 @@ public fun Plot.plotDeviceProperty(
|
|||||||
maxAge: Duration = 1.hours,
|
maxAge: Duration = 1.hours,
|
||||||
maxPoints: Int = 800,
|
maxPoints: Int = 800,
|
||||||
minPoints: Int = 400,
|
minPoints: Int = 400,
|
||||||
|
debounceDuration: Duration = 10.milliseconds,
|
||||||
coroutineScope: CoroutineScope = device.context,
|
coroutineScope: CoroutineScope = device.context,
|
||||||
configuration: Scatter.() -> Unit = {},
|
configuration: Scatter.() -> Unit = {},
|
||||||
): Job = scatter(configuration).run {
|
): Job = scatter(configuration).run {
|
||||||
val clock = device.context.clock
|
val clock = device.context.clock
|
||||||
val data = TimeData()
|
val data = TimeData()
|
||||||
device.propertyMessageFlow(propertyName).transform {
|
device.propertyMessageFlow(propertyName).debounce(debounceDuration).transform {
|
||||||
data.append(it.time ?: clock.now(), it.value.extractValue())
|
data.append(it.time ?: clock.now(), it.value.extractValue())
|
||||||
data.trim(maxAge, maxPoints, minPoints)
|
data.trim(maxAge, maxPoints, minPoints)
|
||||||
emit(data)
|
emit(data)
|
||||||
@ -109,10 +115,11 @@ private fun <T> Trace.updateFromState(
|
|||||||
maxAge: Duration = 1.hours,
|
maxAge: Duration = 1.hours,
|
||||||
maxPoints: Int = 800,
|
maxPoints: Int = 800,
|
||||||
minPoints: Int = 400,
|
minPoints: Int = 400,
|
||||||
|
debounceDuration: Duration = 10.milliseconds,
|
||||||
): Job {
|
): Job {
|
||||||
val clock = context.clock
|
val clock = context.clock
|
||||||
val data = TimeData()
|
val data = TimeData()
|
||||||
return state.valueFlow.transform<T, TimeData> {
|
return state.valueFlow.debounce(debounceDuration).transform<T, TimeData> {
|
||||||
data.append(clock.now(), it.extractValue())
|
data.append(clock.now(), it.extractValue())
|
||||||
data.trim(maxAge, maxPoints, minPoints)
|
data.trim(maxAge, maxPoints, minPoints)
|
||||||
}.onEach {
|
}.onEach {
|
||||||
@ -127,9 +134,10 @@ public fun <T> Plot.plotDeviceState(
|
|||||||
maxAge: Duration = 1.hours,
|
maxAge: Duration = 1.hours,
|
||||||
maxPoints: Int = 800,
|
maxPoints: Int = 800,
|
||||||
minPoints: Int = 400,
|
minPoints: Int = 400,
|
||||||
|
debounceDuration: Duration = 10.milliseconds,
|
||||||
configuration: Scatter.() -> Unit = {},
|
configuration: Scatter.() -> Unit = {},
|
||||||
): Job = scatter(configuration).run {
|
): Job = scatter(configuration).run {
|
||||||
updateFromState(context, state, extractValue, maxAge, maxPoints, minPoints)
|
updateFromState(context, state, extractValue, maxAge, maxPoints, minPoints, debounceDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -139,9 +147,10 @@ public fun Plot.plotNumberState(
|
|||||||
maxAge: Duration = 1.hours,
|
maxAge: Duration = 1.hours,
|
||||||
maxPoints: Int = 800,
|
maxPoints: Int = 800,
|
||||||
minPoints: Int = 400,
|
minPoints: Int = 400,
|
||||||
|
debounceDuration: Duration = 10.milliseconds,
|
||||||
configuration: Scatter.() -> Unit = {},
|
configuration: Scatter.() -> Unit = {},
|
||||||
): Job = scatter(configuration).run {
|
): Job = scatter(configuration).run {
|
||||||
updateFromState(context, state, { asValue() }, maxAge, maxPoints, minPoints)
|
updateFromState(context, state, { asValue() }, maxAge, maxPoints, minPoints, debounceDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -151,7 +160,8 @@ public fun Plot.plotBooleanState(
|
|||||||
maxAge: Duration = 1.hours,
|
maxAge: Duration = 1.hours,
|
||||||
maxPoints: Int = 800,
|
maxPoints: Int = 800,
|
||||||
minPoints: Int = 400,
|
minPoints: Int = 400,
|
||||||
|
debounceDuration: Duration = 10.milliseconds,
|
||||||
configuration: Bar.() -> Unit = {},
|
configuration: Bar.() -> Unit = {},
|
||||||
): Job = bar(configuration).run {
|
): Job = bar(configuration).run {
|
||||||
updateFromState(context, state, { asValue() }, maxAge, maxPoints, minPoints)
|
updateFromState(context, state, { asValue() }, maxAge, maxPoints, minPoints, debounceDuration)
|
||||||
}
|
}
|
@ -3,17 +3,28 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"collapsed": true
|
|
||||||
},
|
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"USE(ControlsJupyter())"
|
"//import space.kscience.controls.jupyter.ControlsJupyter\n",
|
||||||
|
"\n",
|
||||||
|
"//USE(ControlsJupyter())\n",
|
||||||
|
"USE{\n",
|
||||||
|
" repositories{\n",
|
||||||
|
" maven(\"https://repo.kotlin.link\")\n",
|
||||||
|
" }\n",
|
||||||
|
" dependencies{\n",
|
||||||
|
" implementation(\"space.kscience:controls-jupyter-jvm:0.3.0-dev-2\")\n",
|
||||||
|
" }\n",
|
||||||
|
"}"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"collapsed": false
|
||||||
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"class LinearDrive(\n",
|
"class LinearDrive(\n",
|
||||||
@ -34,14 +45,14 @@
|
|||||||
" val position by property(state)\n",
|
" val position by property(state)\n",
|
||||||
" var target by mutableProperty(pid.mutablePropertyAsState(Regulator.target, 0.0))\n",
|
" var target by mutableProperty(pid.mutablePropertyAsState(Regulator.target, 0.0))\n",
|
||||||
"}\n"
|
"}\n"
|
||||||
],
|
]
|
||||||
"metadata": {
|
|
||||||
"collapsed": false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"collapsed": false
|
||||||
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"import kotlin.time.Duration.Companion.milliseconds\n",
|
"import kotlin.time.Duration.Companion.milliseconds\n",
|
||||||
@ -57,14 +68,14 @@
|
|||||||
")\n",
|
")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"val device = context.install(\"device\", LinearDrive(context, state, 0.005, pidParameters))"
|
"val device = context.install(\"device\", LinearDrive(context, state, 0.005, pidParameters))"
|
||||||
],
|
]
|
||||||
"metadata": {
|
|
||||||
"collapsed": false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"collapsed": false
|
||||||
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"\n",
|
"\n",
|
||||||
@ -80,14 +91,14 @@
|
|||||||
" sin(2 * PI * 21 * freq * t + 0.02 * (timeFromStart / pidParameters.timeStep))\n",
|
" sin(2 * PI * 21 * freq * t + 0.02 * (timeFromStart / pidParameters.timeStep))\n",
|
||||||
" }\n",
|
" }\n",
|
||||||
"}"
|
"}"
|
||||||
],
|
]
|
||||||
"metadata": {
|
|
||||||
"collapsed": false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"collapsed": false
|
||||||
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"val maxAge = 10.seconds\n",
|
"val maxAge = 10.seconds\n",
|
||||||
@ -96,16 +107,18 @@
|
|||||||
"VisionForge.fragment {\n",
|
"VisionForge.fragment {\n",
|
||||||
" vision {\n",
|
" vision {\n",
|
||||||
" plotly {\n",
|
" plotly {\n",
|
||||||
" plotNumberState(context, state, maxAge = maxAge) {\n",
|
|
||||||
" name = \"real position\"\n",
|
|
||||||
" }\n",
|
|
||||||
" plotDeviceProperty(device.pid, Regulator.position.name, maxAge = maxAge) {\n",
|
|
||||||
" name = \"read position\"\n",
|
|
||||||
" }\n",
|
|
||||||
" \n",
|
" \n",
|
||||||
" plotDeviceProperty(device.pid, Regulator.target.name, maxAge = maxAge) {\n",
|
" plotDeviceProperty(device.pid, Regulator.target.name, maxAge = maxAge) {\n",
|
||||||
" name = \"target\"\n",
|
" name = \"target\"\n",
|
||||||
" }\n",
|
" }\n",
|
||||||
|
" \n",
|
||||||
|
" plotNumberState(context, state, maxAge = maxAge) {\n",
|
||||||
|
" name = \"real position\"\n",
|
||||||
|
" }\n",
|
||||||
|
" \n",
|
||||||
|
" plotDeviceProperty(device.pid, Regulator.position.name, maxAge = maxAge) {\n",
|
||||||
|
" name = \"read position\"\n",
|
||||||
|
" }\n",
|
||||||
" }\n",
|
" }\n",
|
||||||
" }\n",
|
" }\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -122,23 +135,29 @@
|
|||||||
" }\n",
|
" }\n",
|
||||||
" }\n",
|
" }\n",
|
||||||
"}"
|
"}"
|
||||||
],
|
]
|
||||||
"metadata": {
|
|
||||||
"collapsed": false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"collapsed": false
|
||||||
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"import kotlinx.coroutines.cancel\n",
|
"import kotlinx.coroutines.cancel\n",
|
||||||
"\n",
|
"\n",
|
||||||
"job.cancel()"
|
"job.cancel()"
|
||||||
],
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"collapsed": false
|
"collapsed": false
|
||||||
}
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
@ -156,21 +175,21 @@
|
|||||||
"language": "kotlin",
|
"language": "kotlin",
|
||||||
"name": "kotlin"
|
"name": "kotlin"
|
||||||
},
|
},
|
||||||
"language_info": {
|
|
||||||
"name": "kotlin",
|
|
||||||
"version": "1.9.0",
|
|
||||||
"mimetype": "text/x-kotlin",
|
|
||||||
"file_extension": ".kt",
|
|
||||||
"pygments_lexer": "kotlin",
|
|
||||||
"codemirror_mode": "text/x-kotlin",
|
|
||||||
"nbconvert_exporter": ""
|
|
||||||
},
|
|
||||||
"ktnbPluginMetadata": {
|
"ktnbPluginMetadata": {
|
||||||
"projectDependencies": [
|
"projectDependencies": [
|
||||||
"controls-kt.controls-jupyter.jvmMain"
|
"controls-kt.controls-jupyter.jvmMain"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": "text/x-kotlin",
|
||||||
|
"file_extension": ".kt",
|
||||||
|
"mimetype": "text/x-kotlin",
|
||||||
|
"name": "kotlin",
|
||||||
|
"nbconvert_exporter": "",
|
||||||
|
"pygments_lexer": "kotlin",
|
||||||
|
"version": "1.8.20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
"nbformat_minor": 0
|
"nbformat_minor": 4
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user