Skip to content

Final Project

Functional representation of the vestibulo-oculomotor reflex (VOR) through digital manufacturing

Research

The vestibulo-ocular reflex is part of the balance system, the balance system is made up of three elements, using a language better known in electronic jargon:

a- the sensors
b- the processors
c- the actuators

The sensors: the ear, the eye, and the proprioceptors of muscles and tendons

The processors: the sensory nuclei and the motor nuclei at the level of the central nervous system

The actuators: the muscles and ligaments.

This image below shows the elements of the balance system with the corresponding electronic component

Component_system_balance

The balance is basically given by two reflex:

Vestibular-espinal reflex
The vestibulo-ocular reflex

The final project focuses on the ocular vestibule reflex that is defined as:

The vestibulo-ocular reflex (VOR) is a reflex acting to stabilize gaze during head movement, with eye movement due to activation of the vestibular system. The reflex acts to stabilize images on the retinas of the eye during head movement. Gaze is held steadily on a location by producing eye movements in the direction opposite that of head movement.

The components of the vestibulo-ocular reflex are:

The ears
nuclei located in the central nervous system
The muscles and ligaments of the eye

The image below shows the components of the vestibulo-oculomotor reflex.

VOR

For more details you can to see: Principles and practices

Objetive

With this project I intend to recreate the normal function of the vestibulo-ocular reflex approaching the anatomy and physiology of the human being and to obtain a model that can be used for a better understanding of the mechanism.

Project development

The first step was:

Compare the ear sensor with an existing electronic device.
Investigate about microprocessors, libraries and codes (our model was the Arduino Uno) and
As an actuator, we initially think of servomotors

Sensor:

We must remember that the ear, in its participation in balance, detects movement and gravity. The angular accelerations are given by the semicircular canals and the linear accelerations by the utricle and saccule.

Reference on planes or axes of the ear and the terms roll, yaw and pitch

The electronic device that seemed similar to the ear was the MPU6050.

Tutorial MPU6050

MEMS tecnology

Image below illustrating the three axes (Roll, Pitch and Yaw) captured by the MPU6050 and the ear

Roll_Pitch_yaw

For angular movements:

The ear has three semicircular canals oriented in the three planes: superior, posterior and lateral semicircular canal. The superior and posterior canals are oriented in the vertical plane and at an angle of 90 degrees to each other, open outwards, and the lateral semicircular canal is oriented almost in the horizontal plane. Remembering that exactly the same anatomy is repeated on the contralateral side but in the opposite direction. Physiologically, they work in synchrony: the upper right with the rear left, the upper left with the rear right, and the sides in the same horizontal plane. (Ilustred below). In this way, when the head turns in any of the planes, one of them is always stimulated and the other is inhibited.

LARP-plane: Left Anterior Right Posterior
RALP-plane: Right Anterior Left Posterior
RH: Right horizontal
LH: Left horizontal Observation: Anterior = Superior

Semicircular_chanels

For linear movements:

The ear has two sensors: the utricle and the saccule, which are also located in the horizontal and vertical planes, respectively. These contain some “pebbles” or otoconia on their surface that, through a mechanism of inertia, cause an alteration of the surface, stimulating or inhibiting the sensory cells on which they rest.

Macula_linear_aceleration

I was struck by the similarity with the spring mass mechanism of the MPU6050 sensor. Both use a mass that in the case of the ear would be the otoconia and the gel that surrounds them would be like the springs. (See image above)

Referencia for sensor MPU6050 caracteristic

Microprocesor:

Our model device is the Arduino Uno. The new board will use the AtMega 328 which is the same as the Arduino uno.

atMega328

actuator

The sg90 servomotor is chosen because it is economical and effective for what we want to demonstrate or illustrate, which is the movement of the eyes in a single plane, the horizontal, in response to the MPU6050 sensor.

sg90

The following steps were carried out in parallel following the program of the FABLAB course. For reasons of order, I choose to start with the first diagrams of the 2D and 3D design process and then expose the part of the electronic design and creation process and programming.

2D and 3D Modeling Process

Image below illustrating the original idea: two sensor MPU6050 in both sides, the microprocesor in the midle and two servomotors in the front

medeling_paper

Image below illustrating the 3D modeling in tinkerkad

Tinkerkad

Image below illustrating head modeling whit Blender software

Modeling_head

We decided to use part of the head that obviously includes the opening of the eyes and the nose. First we decided to print on the face so that the supports don’t mess up the quality of the print, but later we did another full print, but cut as shown in the photo above so that the face is facing up during printing and also in this way we obtain rear and upper space to place the electronic components

Head_cuted

3D printin result 3D_printing

File: Face_Designe

The eyes

The eyes were designed in blender and then exported in stl format for 3D printing. We use two types of printers: LCD with resin and PLA plus, both with good results.

For the iris and the pupil we use adhesive paper

We glue the servo adapter itself to the base of the eye

Eyes_PLA

Eyes_LCD_resina

Craneo_eyes

The Files you can trouve in Week 6

The platform for internal components:

Firs I wrote on paper the steps to follow and the elements to use to order the thought

I made a design on paper and then on cardboard to have greater accuracy of where to place each component and the relationship of each of them is size, height, etc.

This way of working helped me a lot to center or focus my ideas

Finally, using the fusion 360 program, I design the platform.

Photo below with the assembly steps

In paper and cardboard Paper_cardboard

Laser cut Laser_resulted

The upper platform we decided to make it with 3D printing. We included in the design the columns, which would be fixed with screws in the lower platform.

3Dpintin_superior_plataform

File superior plataform

The laser cut lower platform is painted white

inferor_plataform

Since the screws on the underside of the bottom deck were protruding, I cut another one the same size but with larger internal holes, thus achieving a smooth bottom surface.

The rotating base

rotating_base

Craneo_base_acopled

Third step: electronic production and design

Librery and Codes

Using the Arduino uno or the nano, we look for libraries for the use of the sensor and codes that manage to read the information that they capture:

The first attempts to capture information from the MPU6050 sensors were made during the week input device

With the first codes I can’t get exact information. I needed the value of the offset angle on the Z axis

Of all the codes that we look for we liked this:

This tutorial for use MPU6050 like gimbal it was vary usefull for me.

Wire code for MPU6050:

/*
   Arduino and MPU6050 Accelerometer and Gyroscope Sensor Tutorial
   by Dejan, https://howtomechatronics.com
*/

#include <Wire.h>
const int MPU = 0x68; // MPU6050 I2C address
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY, gyroAngleZ;
float roll, pitch, yaw;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY, GyroErrorZ;
float elapsedTime, currentTime, previousTime;
int c = 0;
void setup() {
  Serial.begin(19200);
  Wire.begin();                      // Initialize comunication
  Wire.beginTransmission(MPU);       // Start communication with MPU6050 // MPU=0x68
  Wire.write(0x6B);                  // Talk to the register 6B
  Wire.write(0x00);                  // Make reset - place a 0 into the 6B register
  Wire.endTransmission(true);        //end the transmission
  /*
  // Configure Accelerometer Sensitivity - Full Scale Range (default +/- 2g)
  Wire.beginTransmission(MPU);
  Wire.write(0x1C);                  //Talk to the ACCEL_CONFIG register (1C hex)
  Wire.write(0x10);                  //Set the register bits as 00010000 (+/- 8g full scale range)
  Wire.endTransmission(true);
  // Configure Gyro Sensitivity - Full Scale Range (default +/- 250deg/s)
  Wire.beginTransmission(MPU);
  Wire.write(0x1B);                   // Talk to the GYRO_CONFIG register (1B hex)
  Wire.write(0x10);                   // Set the register bits as 00010000 (1000deg/s full scale)
  Wire.endTransmission(true);
  delay(20);
  */
  // Call this function if you need to get the IMU error values for your module
  calculate_IMU_error();
  delay(20);
}
void loop() {
  // === Read acceleromter data === //
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  //For a range of +-2g, we need to divide the raw values by 16384, according to the datasheet
  AccX = (Wire.read() << 8 | Wire.read()) / 16384.0; // X-axis value
  AccY = (Wire.read() << 8 | Wire.read()) / 16384.0; // Y-axis value
  AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0; // Z-axis value
  // Calculating Roll and Pitch from the accelerometer data
  accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) - 0.58; // AccErrorX ~(0.58) See the calculate_IMU_error()custom function for more details
  accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) + 1.58; // AccErrorY ~(-1.58)
  // === Read gyroscope data === //
  previousTime = currentTime;        // Previous time is stored before the actual time read
  currentTime = millis();            // Current time actual time read
  elapsedTime = (currentTime - previousTime) / 1000; // Divide by 1000 to get seconds
  Wire.beginTransmission(MPU);
  Wire.write(0x43); // Gyro data first register address 0x43
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 4 registers total, each axis value is stored in 2 registers
  GyroX = (Wire.read() << 8 | Wire.read()) / 131.0; // For a 250deg/s range we have to divide first the raw value by 131.0, according to the datasheet
  GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
  GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
  // Correct the outputs with the calculated error values
  GyroX = GyroX + 0.56; // GyroErrorX ~(-0.56)
  GyroY = GyroY - 2; // GyroErrorY ~(2)
  GyroZ = GyroZ + 0.79; // GyroErrorZ ~ (-0.8)
  // Currently the raw values are in degrees per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees
  gyroAngleX = gyroAngleX + GyroX * elapsedTime; // deg/s * s = deg
  gyroAngleY = gyroAngleY + GyroY * elapsedTime;
  yaw =  yaw + GyroZ * elapsedTime;
  // Complementary filter - combine acceleromter and gyro angle values
  roll = 0.96 * gyroAngleX + 0.04 * accAngleX;
  pitch = 0.96 * gyroAngleY + 0.04 * accAngleY;

  // Print the values on the serial monitor
  Serial.print(roll);
  Serial.print("/");
  Serial.print(pitch);
  Serial.print("/");
  Serial.println(yaw);
}
void calculate_IMU_error() {
  // We can call this funtion in the setup section to calculate the accelerometer and gyro data error. From here we will get the error values used in the above equations printed on the Serial Monitor.
  // Note that we should place the IMU flat in order to get the proper values, so that we then can the correct values
  // Read accelerometer values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    AccX = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    AccY = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    // Sum all readings
    AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
    AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
    c++;
  }
  //Divide the sum by 200 to get the error value
  AccErrorX = AccErrorX / 200;
  AccErrorY = AccErrorY / 200;
  c = 0;
  // Read gyro values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x43);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    GyroX = Wire.read() << 8 | Wire.read();
    GyroY = Wire.read() << 8 | Wire.read();
    GyroZ = Wire.read() << 8 | Wire.read();
    // Sum all readings
    GyroErrorX = GyroErrorX + (GyroX / 131.0);
    GyroErrorY = GyroErrorY + (GyroY / 131.0);
    GyroErrorZ = GyroErrorZ + (GyroZ / 131.0);
    c++;
  }
  //Divide the sum by 200 to get the error value
  GyroErrorX = GyroErrorX / 200;
  GyroErrorY = GyroErrorY / 200;
  GyroErrorZ = GyroErrorZ / 200;
  // Print the error values on the Serial Monitor
  Serial.print("AccErrorX: ");
  Serial.println(AccErrorX);
  Serial.print("AccErrorY: ");
  Serial.println(AccErrorY);
  Serial.print("GyroErrorX: ");
  Serial.println(GyroErrorX);
  Serial.print("GyroErrorY: ");
  Serial.println(GyroErrorY);
  Serial.print("GyroErrorZ: ");
  Serial.println(GyroErrorZ);
} 

Monitor_serial_MPU6050_Zaxe

Once the sensor test is done, we look for codes to move the servo

Time to be honest, sorry ! Since I lost part of my documentation, I cannot illustrate all the misfortunes that occurred. And since I don’t remember where I got the code to control the servo, but I do remember that Abdon used that code and introduced it to the sensor code.

To get the code of the servo I did a reverse engineering, I extracted it from the CODE of the sensor where I had included the servo and I got this code

This is the code I extracted from the sensor code related to servo control.

Photo of of CODE incorporated First_CODE In_void_setup

I’m sorry, I’m sorry and I’m sorry again. I didn’t want to leave this hole in my documentation. I made a new documentation and thanks to that some things were clarified, I reviewed others and new cumbersome situations appeared, but well I think this is something usual (errors and successes)

If you take some time and visit my week 11 and week 13 (Output and Input device) I have updated my documentation and documented new experiences with the servos and the sensor. As a catharsis I tell you that I burned one of my sensors by connecting the GND and VCC backwards. (things that happen the instructors tell me)

And we incorporate it into the sensor code making some adjustments so that the servomotor performs the inverse movement to the angle of rotation of the sensor.

We made with Abdon a reasoning on paper and then write the code. We needed the servo to perform exactly the inverse movement to the angle of rotation of the sensor

Anotation_paper

In_void_loop

Code incorpored to Code MPU 6050

#include <Wire.h>
#include <Servo.h>   

Servo sg901; 
Servo sg902;         

int servo_pin5 = 5;
int servo_pin6 = 6;
const int MPU = 0x68; // MPU6050 I2C address
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY, gyroAngleZ;
float roll, pitch, yaw;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY, GyroErrorZ;
float elapsedTime, currentTime, previousTime;
int c = 0;
void setup() {
 sg901.attach ( servo_pin5 );
 sg902.attach ( servo_pin6 );
 Serial.begin(19200);
 Wire.begin();                      // Initialize comunication
 Wire.beginTransmission(MPU);       // Start communication with MPU6050 // MPU=0x68
 Wire.write(0x6B);                  // Talk to the register 6B
 Wire.write(0x00);                  // Make reset - place a 0 into the 6B register
 Wire.endTransmission(true);        //end the transmission
 /*
 // Configure Accelerometer Sensitivity - Full Scale Range (default +/- 2g)
 Wire.beginTransmission(MPU);
 Wire.write(0x1C);                  //Talk to the ACCEL_CONFIG register (1C hex)
 Wire.write(0x10);                  //Set the register bits as 00010000 (+/- 8g full scale range)
 Wire.endTransmission(true);
 // Configure Gyro Sensitivity - Full Scale Range (default +/- 250deg/s)
 Wire.beginTransmission(MPU);
 Wire.write(0x1B);                   // Talk to the GYRO_CONFIG register (1B hex)
 Wire.write(0x10);                   // Set the register bits as 00010000 (1000deg/s full scale)
 Wire.endTransmission(true);
 delay(20);
 */
 // Call this function if you need to get the IMU error values for your module
 calculate_IMU_error();
 delay(20);
}
void loop() {
 // === Read acceleromter data === //
 Wire.beginTransmission(MPU);
 Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
 Wire.endTransmission(false);
 Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
 //For a range of +-2g, we need to divide the raw values by 16384, according to the datasheet
 AccX = (Wire.read() << 8 | Wire.read()) / 16384.0; // X-axis value
 AccY = (Wire.read() << 8 | Wire.read()) / 16384.0; // Y-axis value
 AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0; // Z-axis value
 // Calculating Roll and Pitch from the accelerometer data
 accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) - AccErrorX ; // AccErrorX ~(0.58) See the calculate_IMU_error()custom function for more details
 accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) - AccErrorY ; // AccErrorY ~(-1.58)
 // === Read gyroscope data === //
 previousTime = currentTime;        // Previous time is stored before the actual time read
 currentTime = millis();            // Current time actual time read
 elapsedTime = (currentTime - previousTime) / 1000; // Divide by 1000 to get seconds
 Wire.beginTransmission(MPU);
 Wire.write(0x43); // Gyro data first register address 0x43
 Wire.endTransmission(false);
 Wire.requestFrom(MPU, 6, true); // Read 4 registers total, each axis value is stored in 2 registers
 GyroX = (Wire.read() << 8 | Wire.read()) / 131.0; // For a 250deg/s range we have to divide first the raw value by 131.0, according to the datasheet
 GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
 GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
 // Correct the outputs with the calculated error values
 GyroX = GyroX - GyroErrorX ; // GyroErrorX ~(-0.56)
 GyroY = GyroY - GyroErrorY ; // GyroErrorY ~(2)
 GyroZ = GyroZ - GyroErrorZ ; // GyroErrorZ ~ (-0.8)
 // Currently the raw values are in degrees per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees
 gyroAngleX = gyroAngleX + GyroX * elapsedTime; // deg/s * s = deg
 gyroAngleY = gyroAngleY + GyroY * elapsedTime;
 yaw =  yaw + GyroZ * elapsedTime;
 // Complementary filter - combine acceleromter and gyro angle values
 roll = 0.96 * gyroAngleX + 0.04 * accAngleX;
 pitch = 0.96 * gyroAngleY + 0.04 * accAngleY;

 // Print the values on the serial monitor
 Serial.print(roll);
 Serial.print("/");
 Serial.print(pitch);
 Serial.print("/");
 Serial.println(yaw);
 int angulo=yaw-90;
 if(angulo<0){
  angulo=angulo * (-1);
 }
 sg901.write (angulo);
 sg902.write (angulo);
}
void calculate_IMU_error() {
 // We can call this funtion in the setup section to calculate the accelerometer and gyro data error. From here we will get the error values used in the above equations printed on the Serial Monitor.
 // Note that we should place the IMU flat in order to get the proper values, so that we then can the correct values
 // Read accelerometer values 200 times
 while (c < 200) {
   Wire.beginTransmission(MPU);
   Wire.write(0x3B);
   Wire.endTransmission(false);
   Wire.requestFrom(MPU, 6, true);
   AccX = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
   AccY = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
   AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
   // Sum all readings
   AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
   AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
   c++;
 }
 //Divide the sum by 200 to get the error value
 AccErrorX = AccErrorX / 200;
 AccErrorY = AccErrorY / 200;
 c = 0;
 // Read gyro values 200 times
 while (c < 200) {
   Wire.beginTransmission(MPU);
   Wire.write(0x43);
   Wire.endTransmission(false);
   Wire.requestFrom(MPU, 6, true);
   GyroX = Wire.read() << 8 | Wire.read();
   GyroY = Wire.read() << 8 | Wire.read();
   GyroZ = Wire.read() << 8 | Wire.read();
   // Sum all readings
   GyroErrorX = GyroErrorX + (GyroX / 131.0);
   GyroErrorY = GyroErrorY + (GyroY / 131.0);
   GyroErrorZ = GyroErrorZ + (GyroZ / 131.0);
   c++;
 }
 //Divide the sum by 200 to get the error value
 GyroErrorX = GyroErrorX / 200;
 GyroErrorY = GyroErrorY / 200;
 GyroErrorZ = GyroErrorZ / 200;
 // Print the error values on the Serial Monitor
 Serial.print("AccErrorX: ");
 Serial.println(AccErrorX);
 Serial.print("AccErrorY: ");
 Serial.println(AccErrorY);
 Serial.print("GyroErrorX: ");
 Serial.println(GyroErrorX);
 Serial.print("GyroErrorY: ");
 Serial.println(GyroErrorY);
 Serial.print("GyroErrorZ: ");
 Serial.println(GyroErrorZ);

}

Video showing the mechanism that we want the servomotor to perform based on the angle of rotation of the sensor on a provisional platform mounted on cardboard.

Video with two servos

Design of the electronic circuit that emulates the central nervous system

I already have the programmer designed and working and the board with the atMega 328 is also designed as part of the task V and Task 11 respectively

For which we use the At mega 328, which has more pins, which makes it possible to integrate sensory and motor information.

Danduino

Assembly

Now it’s time to put all the pieces together and make it work

Danduino_conection

MPU6060 Sensor Pin Analogic 5 (SDA) and Pin 4 (SCL) GND and VCC 5v Pin 2 INT (interuptor) I still don’t know if it worked

mpu6050 CONECTION

MPU6050 conection

Servomotors actuator Pin digital 5 y 6 GND and VCC

Servomotor conection

vista3

Components on their corresponding platforms

vista2

Project working properly Proyect working

Bill of material and cost

For the fase: PLA PLUS and for the eyes the Resin (3D Printer Curable Resine)

A full 1.75mm PLA plus thread costs $20
High Precision UV Curing 3D Resin costs $31

For the internal supports: I used 3 mm MDF wood and PLA plus.

12 pieces of MDF Wood of 12 x 24 inches of 3 mm costs $39

For the main external support: I used 13mm laminated wood

A 3 x 1.5 meter piece of 15mm thick plywood costs approximately $20

For the integrated circuit the copper plates and welding of the components with tin

One copper plates cost $5

I used a MPU 6050 sensor and two sg90 servo motors

MPU 6050 sensor cost $10

Servomotor sg90 cost 2$

For the rotary motion I used a ball bearing embedded in a circular plastic that was originally made for a pulley

One ball bearing cost $8

Other materials:

white synthetic Paint cost $30
wood screws
Glue for Wood

Materials such as PLA plus, Resin, wood, copper plate were materials shared with other projects, which could correspond to 1/5 of the total value.
The materials that I did have to acquire and absorb the expense were: the MPU 6050 and the synthetic white paint.
The microcomponents do not have a very high cost and considering their variety I do not see the need to detail them

Files:

2D and 3D Modeling Process

Internal plataforms

Electronics design

Future proyect

This does not exactly emulate the anatomy of the human being, considering that at the central level there are nuclei on each side that receive information from the ear on the corresponding side.

I think, although I clarify that electronics is not my forte, that the atMega328 could process and simulate the two right and left cores and it is only a matter of code (I don’t know) and there would be no need to have two right and left microprocessors

The same thought would apply to the actuator function of the microprocessor.
As we can see in the figure, the motor nuclei are independent from the sensory nuclei, but they are interconnected and to further complicate the connections that go from the sensory nuclei to the motor nuclei, they are crossed.

Below graph of the vestibulo-oculomotor reflex

VOR

More information about VOR


Last update: July 12, 2022