数据分析是科学、技术、工程和数学 (STEM) 领域的一个关键问题。可视化数据的能力对于理解数据并得出结论至关重要。因此,视力受损的学生处于不利地位,可能会不鼓励从事科学事业。已经开发了许多工具来克服这一挑战,但是,它们通常依赖于口头描述或静态触觉图片,这可能会受到限制。据报道,触觉和听觉刺激的结合可以提高盲人学生对几何等视觉主题的学习[1] 。很明显,需要让视障者更容易接触到 STEM 科目,因此在这个项目中,我们的目标是构建一个廉价的3D 跟踪器,用于动态听觉数据表示.
这个想法是构建一个将数学函数(在真实的线、平面或 3D 空间上)转换为声音的设备。我们可以通过使用MGC3130芯片的 3D 跟踪传感器(例如Flick或Skywriter )检测用户手的位置来导航空间。然后可以将记录的位置用作数学函数的输入,该数学函数的输出可以处理成声音信号。通过将 3D-tracker 连接到Arduino或RaspberryPi可以轻松完成数据采集,然后我们可以处理数据并将输出发送到声音处理软件,例如Supercollider或CSound 。
由于我们打算将其作为一种包容性技术,我们还将尝试包括视觉和触觉反馈。为了可视化数据,我们可以使用Processing ,这是一种专门为视觉艺术家开发的简单编程语言。触觉反馈可以通过不同的方式来实现。一方面,我们可以使用超声波换能器在半空中创建触觉反馈(Ultraino )。另一方面,我们可以使用振动马达来模拟不同的纹理(Hap2U )。
目前,我们仍然不确定哪种是包含触觉反馈的最佳方式,但我们会不断更新。
我们项目所需的软件适用于 Windows、Mac 和 Linux 系统,但不适用于 Chrome OS。如果您使用的是 Chromebook,有一些选项可以解决它。第一个是使用Arduino和Processing的网络编辑器,不幸的是,Supercollider 没有在线选项。另一种选择是CSound Web IDE ,但我们发现 Supercollider 更易于使用。第二种选择是安装 Linux,可以通过多种方式完成。最简单的一种是启用原生 Chrome OS Linux(测试版)虚拟机并安装 Linux 应用程序。这种方法的问题是虚拟机仍然存在一些问题,特别是设置音频不是那么容易,使用 Supercollider 几乎是不可能的。在 Chromebook 上安装 Linux 的另一种方法是使用Crouton ,它为我们提供了一个可以更轻松地配置声音的 Linux 发行版。让 Supercollider 服务器运行还有一个额外的步骤,将JACK 转发到 CRAS 。请注意,此方法需要启用开发者模式,这会使您的 Chromebook 保修失效。
客户端-服务器架构
第一步是获取位置数据。我们使用Pi Supply的 Flick Large ,但 Pimoroni 的Skywriter可以以相同的方式连接,实际上,两者都使用相同的 Arduino 库。Flick 有 8 个公针,两个用于电源(VCC、GND),两个用于数据通信(SDA、SCL)和四个数字针(TS、RESET、LED1、LED2)。前四个必须连接到Elegoo (Arduino) UNO R3中的相应引脚,其他四个可以连接到 Elegoo 中的任何数字引脚。下面我们展示了示意图连接,Skywriter 应该以类似方式连接,除了在这种情况下不存在的 LED 引脚。
为了控制 Flick,我们使用 Pimoroni 的Skywriter 库。下载后,我们通过将 Skywriter 目录复制到 Arduino 库目录来安装库。我们现在可以启动 Arduino IDE 并开始编码。一个简单的例子是跟踪位置并将其显示在串行监视器上。为了启用串行端口上的通信,我们包括接下来的两个库
#include
#include
然后我们初始化Arduino板如下
void setup() {
Serial.begin(9600); //Initialise serial communication at 9600 bds
while(!Serial){}; //We wait for the data serial port to start
Serial.println("Hello world!"); //The port is ready!
Skywriter.begin(10,11); //Initialise the Flick with the pins TS=D10 and RESET=D11
Skywriter.onXYZ(handle_xyz); //This method records the position on the Flick and the function handle_xyz manipulates the data.
//We can initialise the LED pins as follows
//pinMode (9, OUTPUT); //Red LED
//pinMode (8, OUTPUT); //Green LED
//We can turn on the green LED to know when the Flick is ready
//digitalWrite (8, HIGH);
}
在这种情况下,Arduino 代码的循环部分将只包含一行
void loop() {
Skywriter.poll(); //Check if the status of the Flick has changed
}
此行检查 Flick 的状态是否已更改。如果没有发生任何事情,我们将观察到串行监视器上没有发生任何事情。如果状态发生变化,例如将我们的手放在棋盘上,那么我们将观察到一些动作。最简单的方法是将位置打印为串行监视器中的元组。以下功能可以解决问题
void handle_xyz(unsigned int x, unsigned int y, unsigned int z){
char buf[17]; //An array of 17 characters 5 for each coordinate and two delimiters
sprintf(buf, "%05u:%05u:%05u",x,y,z); //Record the position on the Flick
Serial.println(buf); //Print the position to the serial port, one line at a time
}
编译代码并将其上传到 Arduino 板后,我们打开串口监视器,应该会看到该行Hello world!
(确保监视器的速度与我们用于初始化串口的速度相匹配,本例中为 9600 bds)。如果我们将手放在 Flick 上并在它周围移动,那么我们应该会看到该位置一次打印一行。
为了获得代表数据的声音,我们首先需要让 Arduino 和 Supercollider 相互交谈。我们通过串口通信来实现这一点,并通过一个简单的例子来演示。我们将在 Supercollider 中播放一个简单的 Synth,并使用 Flick 上的气轮手势控制它的频率。
首先是Arduino代码。我们像以前一样初始化电路板,用方法更改行Skywriter.onXYZ();
Skywriter.onAirwheel(handleAirwheel); //This method records an airwheel event and returns a positive/negative value if clockwise/counterclockwise rotation is detedted
我们定义一个变量来控制正弦振荡器的频率:
int freq=440;
当检测到顺时针或逆时针旋转时,该功能handleAirwheel
会增加或减少频率的值:
void handleAirwheel (int delta){
if(delta>0){ //Increase the frequency if clockwise rotation
if(freq<1000){ //Upper cutoff
freq=freq+1;
}
}else if(delta<0){//Decrease the frequency if counterclockwise rotation
if(freq>1){ //Lower cutoff
freq=freq-1;
}
}
}
我们在代码的循环部分将频率的值打印到串口:
void loop() {
Skywriter.poll(); //Check if the status of the Flick has changed
Serial.print(freq); //Print frequency to serial port
Serial.print('a'); //Print a delimiter character
delay(1);
}
我们打印一个分隔符a
来轻松处理数据,并打印一个小的延迟以避免 Supercollider 中的服务器崩溃。一旦我们的 Arduino 代码在板上运行,我们关闭 IDE,因为只有一个设备可以同时与串行端口通信。然后我们打开 Supercollider IDE 并启动服务器。我们检查下一行可用的串行端口
SerialPort.devices; //Check the available ports
帖子窗口(右下角)上的输出应该类似于[/dev/ttyACM0]
. 然后我们定义一个新的 Serial 变量运行下一行
~port = SerialPort.new("/dev/ttyACM0",9600);
第一个参数对应端口的名称,第二个参数是速度,确保这与 Arduino 代码中的速度相匹配。然后我们需要创建一个从 Flick 读取数据并将其存储在 Supercollider 变量中的函数
(
~charArray = []; //An array to store the characters printed to the serial port
~getValues = Routine.new({ //The code inside the routine loops indefinitely
var ascii; //Supercollider read the characters in the serial port as ascii, so we need to convert them to numbers
{
ascii = ~port.read.asAscii; //We read the characters one by one and convert them to digits
if(ascii.isDecDigit, {
~charArray = ~charArray.add(ascii)
});
if(ascii == $a, { //We stop reading the characters when Supercollider finds the delimiter 'a'
~val = ~charArray.collect(_.digit).convertDigits; //We collect and combine the digits into a number
~charArray = []; //We empty the array
})
}.loop;
}).play
)
然后我们定义最简单的合成器,一个正弦波
(
SynthDef.new(\sineWave, { //Name of the Synth
arg freq = 440; //Frequency
var sig; //Output
sig = SinOsc.ar(freq,0,1); //Sine oscillator with frequency freq, phase 0 and amplitude 1
Out.ar(0,sig); //send output signal to the left speaker
}).add;
)
我们播放运行下一行的合成器
~synth = Synth(\sineWave, [\freq, 440]);
然后我们创建一个例程来使用来自 Flick 的数据修改振荡器的频率
(
~control = Routine.new({
{
~synth.set(\freq, ~val.linexp(1,1000,20,2000)); //exponential map from (1;1000) to (20,2000)
0.01.wait;
}.loop;
}).play;
)
该例程~control
以指数方式将打印到串行端口的值范围映射到一个范围(maximum frequency, minimum frequency)
。当人类以对数方式感知频率时,我们使用指数图。
如果一切正常,我们应该听到这样的声音
当我们玩完合成器后,我们停止控制例程并释放服务器并停止串行通信,评估以下行
~control.stop;
~synth.free;
~port.stop;
将 Arduino 板与 Processing 进行通信使我们能够可视化我们的数据。通讯也是通过串口实现的。我们将通过一个使用手势检测的简单示例来展示如何做到这一点。Arduino代码和之前一样,这次将Skywriter.onAirwheel();
方法替换为
Skywriter.onGesture(handleGesture); //This method records a gesture event up/down/right/left
其中函数handleGesture
定义如下
void handleGesture(unsigned char type){
Serial.println(type,DEC); //Prints 2 left-right, 3 right-left, 4 bottom-top, 5 top-bottom swipe
}
现在要读取处理中的数据,我们打开 IDE,加载serial
库,并设置草图窗口
import processing.serial.*;//load serial library
Serial myPort;//define serial variable
void setup(){
size(400,400);//size of the sketch in pixels
background(255);//white background
myPort = new Serial(this, "/dev/ttyACM0",9600);//Use the same serial port and communication speed used in the Arduino code
myPort.bufferUntil('\n');//Wait for the port to be ready
}
该变量mySerial
必须定义为从我们在 Arduino 代码中使用的相同端口读取,并以相同的速度读取数据。一旦我们建立通信,我们就可以使用串口中的数据。我们将创建一个带有移动粒子的简单草图,该粒子可以在 +/- x 和 y 方向上加速,具体取决于我们在 Flick 上滑动的方向。要读取mySerial
端口中的数据,我们使用函数serialEvent()
,该函数会在串行端口中有新信息可用时进行注册并对其进行处理,例如
void serialEvent (Serial myPort){
direction=int(float(myPort.readStringUntil('\n')));//Read the data in the serial port as a string one line at a time, and converts it into a integer
gravity();//Changes the direction of the acceleration depending on the value stored in direction. 2=right, 3=left, 4=up, 5 =down.
}
在这种情况下,我们一次读取mySerial
端口中的一行数据(这是'\n'
字符所指示的)并将其作为整数存储在变量中direction
。该函数gravity()
会根据 o 中存储的值更改加速度,direction
例如,如果我们向右滑动,粒子应该开始向右“下落”,如下面的视频所示。您可以在代码部分找到草图的详细信息。
为了听到和可视化我们的数据,我们需要让 Processing 和 Supercollider 相互交谈。与前面两个例子不同的是,两个平台之间的通信不是通过串口来实现的,而是通过OSC 消息来实现的,它是专门为音乐和表演控制而设计的。我们将展示如何让这两个程序与一个简单的示波器草图进行对话。让我们从处理草图开始。我们首先需要下载oscP5库并安装它。在 IDE 上转到 Sketch->Import Library->Add Library 并查找 oscP5 文件。我们草图的标题和设置部分应如下所示
import netP5.*;
import oscP5.*;
//Declare osc and supercollider ip address
OscP5 osc;
NetAddress supercollider;
void setup(){
size(800,400);
osc = new OscP5(this,12000); //construct object osc, "this" references the Processing sketch and 12000 is the port at which it talks, this can be any number
supercollider = new NetAddress("127.0.0.1", 57120); //construct object supercollider, 127.0.0.1 is the local IP address and 57120 the port
}
我们应该知道我们正在与之交谈的程序的IP地址,在这种情况下,我们正在向supercollider
同一台计算机上运行的程序发送数据,因此我们使用本地IP地址127.0.0.1和端口号57120。可以获取此信息通过评估NetAddr.localAddr;
Supercollider 中的线。我们现在必须在 Supercollider 中建立通信。打开 IDE,我们要做的第一件事是使用本地 IP 和我们用于 OSC 的端口号构造一个 Net Address 对象
~processing = NetAddr.new("127.0.0.1",12000); //Construct object processing at the local address 127.0.0.1 and port number 12000
现在我们将产生一个频率的正弦波,freq
并将这个值发送到处理。我们使用与 Arduino-Supercollider 示例中相同的 Synth,但我们将控制例程更改如下
(
//Change the frequency at random
~fr = rrand(220,3520);
~synth.set(\freq, ~fr);
//Send OSC message to Processing, '/frequency' is the name of the message and ~fr its contents
~processing.sendMsg(
'/frequency', ~fr
);
~fr; //Print the value of the frequency as a sanity check
)
每次我们评估这些线时,我们都会改变正弦波的频率并将其值发送到处理。要接收消息,我们使用 oscEvent() 函数
void oscEvent(OscMessage theOscMessage){
omega=TWO_PI*float(theOscMessage.get(0).intValue())/(440*width); //convert the frequency value sent as a string from supercollider into an integer and then calculate the angular frequency
}
在这种情况下,我们使用 OSC 消息中的数据来绘制一个角频率为 的正弦波omega
,这样 440 Hz 波的一个周期就适合屏幕。我们还想从 Processing 向 Supercollider 发送消息,为此我们使用一个OscMessage
对象。在这种情况下,我们将使用键盘上的向上和向下箭头来增加正弦波的幅度/音量
void keyPressed(){//Registers when a key is pressed and stores it value on the variable key
if(keyCode==UP){//UP,DOWN,RIGHT and LEFT are coded keys
if(amplitude<200){//Increase amplitude
amplitude+=10;
}
}else if(keyCode==DOWN){//Decrease amplitude
if(amplitude>0){
amplitude-=10;
}
}
OscMessage msg = new OscMessage("/amplitude"); //Construct OscMessage object with name /amplitude
msg.add(map(amplitude,0,200,0,1)); //map the amplitude to the [0,1] range and add it to the OSC message
osc.send(msg,supercollider); //send the message
}
我们捕捉到消息并通过评估以下几行来使用它来增加 Supercollider 中的音量
(
//This routine reads the message /amplitude from processing and uses it to control the volume of the sine wave
OSCdef('volume',{
arg msg;//This variable stores the message
~synth.set(\amp,msg[1]);//component [0] contains the OSC address, [1], [2],... contain the values added to the message
},"/amplitude"); //the OSC message we are listening to
)
为了让这个示例运行,我们首先在 Processing 中播放草图,然后评估 Supercollider 代码。特别是,我们必须运行,OSCdef
以便 Supercollider 监听来自 Processing 的消息。在下面的视频中,您可以看到此示例的工作原理,您可以下载代码以查看详细信息。
我们现在准备把它们放在一起并听一些数据。例如,我们可以根据输出信号的音量来推断分布的形状
我们可以通过使用跟踪数据产生振动来增加另一个层次的感觉。为此,我们为传感器构建了一个外壳,它允许我们安装一个显示器,我们可以使用微型振动电机使其振动。由于这需要定制零件,我们将对外壳进行建模并进行 3D 打印。我们可以使用几个免费软件工具来解决这个问题,例如适用于 Linux 的FreeCAD或在线选项Fusion 360和OnShape 。每一个都有几个在线教程和资源,但在我看来,OnShape 是最容易使用和学习最快的. 您可以在附件部分找到 skywriter 原型案例的 stl 文件。这种情况应该允许在传感器顶部安装一块透明的亚克力板,作为“触觉显示器”。我们可以将振动电机粘附在亚克力板上,使用跟踪数据激活。例如在上面的例子中,我们可以根据每个分布中点的局部密度来增加振动强度。
不幸的是,在使用振动反馈对传感器进行测试后,我们发现运动跟踪器在封装时的准确性并不是那么好。因此,跟踪数据是嘈杂的,并没有给出公平的表示。也许,使用不同类型的传感器可能会很方便,例如 Sparkfun 的ZXgesture传感器。
[1] Cryer, H. (2013)。“向盲人和弱视学生教授 STEM 科目:文献回顾和资源”。RNIB 无障碍信息中心,伯明翰:文献综述 #6。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !