-
Posts
21 -
Joined
-
Last visited
Content Type
Profiles
Forums
Events
Everything posted by theperson
-
G2 to Quest Pro (when is better actually better?)
theperson replied to j9murphy's topic in Virtual Reality
Kuject Design 18W Fast Charging Link Cable for Quest 2/Pro/Pico 4 16FT, with Separate Charging Port for Unlimited 120HZ PC/Steam VR Playtime, USB 3.0 Type A to C Cable Accessories for VR Headset https://a.co/d/dIIhYTJ This is the one I have. It has not burned my house down. For the record I have tried both the official expensive link cable and this other charging cable, and they both performed perfectly as far as visuals/bit rates, but the headset battery discharged in ~3hours. -
G2 to Quest Pro (when is better actually better?)
theperson replied to j9murphy's topic in Virtual Reality
Don't do it! The fibre optic cable is not needed. I believe the reason for it is to make the cable slightly thinner and more flexible. You can push the same amount of pixels through a $8 regular usb-c cable. I recommend the "Kuject Design Fast Charging Link Cable (PD version)" from amazon, when paired with a PD wall plug you get unlimited battery life. The official $80 fibre cable only gave me 3 hour battery life. -
Try lowering your Encode Resolution (Width) in either oculus tray tools or the oculus debug tool
-
OpenXR Guide - Deprecated - This time for real (▀̿Ĺ̯▀̿ ̿)
theperson replied to nikoel's topic in Virtual Reality
I have similar juddering with reprojection turned off and can't get a smooth image without reprojection. I've gone as far as a clean windows install. Happens with or without the toolkit installed. If I can get above 90fps the judder stops. I just use reprojection because it feels smooth and the ghosting is less bad, but would love to find a fix. -
OpenXR Guide - Deprecated - This time for real (▀̿Ĺ̯▀̿ ̿)
theperson replied to nikoel's topic in Virtual Reality
Steamvr isnt starting at all, its definitely using the wmr openxr runtime. -
OpenXR Guide - Deprecated - This time for real (▀̿Ĺ̯▀̿ ̿)
theperson replied to nikoel's topic in Virtual Reality
I'll add my experience that I'm getting the extra juddering/ghosting/double vision when moving my head while dropping below 90fps with motion reprojection disabled. I've tried uninstalling & reinstalling steamvr, wmr, openxr, wmr4svr. I've tried what a previous posted said worked for him and set steamvr as the openxr runtime and then set it back to openxr. I've tried installing the openxr toolkit and enabled the overlay, but none has worked. Hopefully someone finds a fix for it. -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
I was having issues with stability where the joystick would randomly stop working and need to be reset, once every couple of days/sessions, so I did a couple of things that seem to have cured it. Firstly, I replaced the chinese pro micro clone with a genuine sparkfun one, and installed their drivers for it. Second, I changed my code to use my own odrive->arduino comms instead of using the ODriveArduino library. This avoids the use of arduino Strings to try and eliminate possible memory issues, clears the serial buffer before reading/writing on it, and overall reduces the number of characters being written on the serial port which makes things faster. I also made sure to give the arduino some "idle" time instead of trying to get as many position updates as possible. I also realized that my settings for the motors and torque commands meant I was saturating my set current limit and was clipping the feedback. You should set the odrive's motor torque constant parameter correctly, and send the commands in units of torque instead of units of current. Here's the updated code. Be warned that it's still pretty confusing and mostly hacked together bull<profanity>. If you've got a question I'd be happy to try and explain. Replace the files in the Fino project with the contents here: Fino.ino: #include "src/Joystick.h" #include "config.h" #include <HardwareSerial.h> // Printing with stream operator helper functions template<class T> inline Print& operator <<(Print &obj, T arg) { obj.print(arg); return obj; } template<> inline Print& operator <<(Print &obj, float arg) { obj.print(arg, 4); return obj; } // ------------------------- // Various global variables // ------------------------- unsigned long lastEffectsUpdate; unsigned long nextJoystickMillis; unsigned long nextEffectsMillis; // -------------------------- // Joystick related variables // -------------------------- #define minX -10000 #define maxX 10000 #define minY -10000 #define maxY 10000 //periodicGain sets the gains for everything that isnt a spring, friction, or damping force const float periodicGain = 0.5; const float extraDampingGain = 0.01; int dampingEnabled = 0; bool is_connected = false; bool forces_requested = false; bool pos_updated = false; bool newData = false; const byte numChars = 32; char receivedChars[numChars]; char tempChars[numChars]; // temporary array for use when parsing float rcvdPos[2]; //uses odrive notation for motors float rcvdVel[2]; //uses odrive notation for motors int16_t pos[2] = {0, 0}; int lastX; int lastY; int lastVelX; int lastVelY; int lastAccelX; int lastAccelY; float scaleFactorX; float scaleFactorY; //because odrive reports in motor rotations we have to do some converting //these are the limits and center for my joystick in odrive-reported-rotations const float maxXRot = 0.13; const float minXRot = -0.11; const float maxYRot = -0.23; const float minYRot = 0.17; const float centerXRot = 0.02; const float centerYRot = -0.03; EffectParams effects[2]; int32_t forces[2] = {0, 0}; Joystick_ Joystick( JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_JOYSTICK, 0, 0, // Button Count, Hat Switch Count true, true, false, // X, Y, Z false, false, false, // Rx, Ry, Rz false, false); // rudder, throttle void setup() { //Makes uploading the new sketch easier, because serial may get flooded //hit the reset pin as it finishes compiling delay(1000); setupJoystick(); // setup timing and run them as soon as possible lastEffectsUpdate = 0; nextJoystickMillis = 0; nextEffectsMillis = 0; dampingEnabled = 0; } void loop(){ //i setup my odrive as motor 1 is x, 0 is y :bigbrain: unsigned long currentMillis; currentMillis = millis(); //wait 2 seconds for encoder signal to calm down and make sure theres no velX funny business if(currentMillis > 2000){ dampingEnabled = 1; } // do not run more frequently than these many milliseconds if (currentMillis >= nextJoystickMillis) { getFeedback(1); getFeedback(0); pos[0] = (int)((rcvdPos[1]-centerXRot)*scaleFactorX); pos[1] = (int)((rcvdPos[0]-centerYRot)*scaleFactorY); updateJoystickPos(); nextJoystickMillis = currentMillis + 8; // we calculate condition forces every 50ms or more frequently if we get position updates if (currentMillis >= nextEffectsMillis || pos_updated) { updateEffects(true); nextEffectsMillis = currentMillis + 50; pos_updated = false; } else { // calculate forces without recalculating condition forces // this helps having smoother spring/damper/friction // if our update rate matches our input device updateEffects(false); } setTorque(1,(forces[0]*(0.1378*7.5/10000.0))+(effects[0].damperVelocity*-extraDampingGain*dampingEnabled)); setTorque(0,(-forces[1]*(0.1378*10/10000.0))+(effects[1].damperVelocity*extraDampingGain*dampingEnabled*2)); } } com.ino: void setOdriveState(int axis, int requested_state) { Serial1 << "w axis" << axis << ".requested_state " << requested_state << '\n'; } void setTorque(int axis, float torque) { Serial1 << "c " << axis << " " << torque << "\n"; } void getFeedback(int motor_number){ //use odrive numbers for motors //clear the input buffer while(Serial1.available()>0){ Serial1.read(); } Serial1 << "f " << motor_number << "\n"; recvFeedback(motor_number); } void recvFeedback(int motor_number) { static byte ndx = 0; char endMarker = '\n'; char rc; static const unsigned long timeout = 10; unsigned long timeout_start = millis(); bool not_timed_out = true; while (newData == false && not_timed_out) { if (Serial1.available() > 0) { rc = Serial1.read(); if (rc != endMarker) { receivedChars[ndx] = rc; ndx++; if (ndx >= numChars) { ndx = numChars - 1; } } else { receivedChars[ndx] = '\0'; // terminate the string ndx = 0; newData = true; } } else if (millis() - timeout_start >= timeout) { not_timed_out = false; //taking too long, abort } } if(newData){ //we must have timed out, so don't copy in new data strcpy(tempChars, receivedChars); char * strtokIndx; // this is used by strtok() as an index strtokIndx = strtok(tempChars, " "); // get the first part - the string rcvdPos[motor_number] = atof(strtokIndx); strtokIndx = strtok(NULL, ","); // this continues where the previous call left off rcvdVel[motor_number] = atof(strtokIndx); // convert this part to a float newData = false; //reset for next read pos_updated = true; } } joystick.ino: void setupJoystick() { scaleFactorX = (2*maxX) / (maxXRot - minXRot); scaleFactorY = (2*maxY) / (maxYRot - minYRot); Joystick.setXAxisRange(minX, maxX); Joystick.setYAxisRange(minY, maxY); Joystick.begin(); Gains gains[FFB_AXIS_COUNT]; gains[0].springGain = 1.0; gains[1].springGain = 1.0; gains[0].frictionGain = 0.25; gains[1].frictionGain = 0.25; gains[0].squareGain = periodicGain; gains[1].squareGain = periodicGain; gains[0].sineGain = periodicGain; gains[1].sineGain = periodicGain; gains[0].triangleGain = periodicGain; gains[1].triangleGain = periodicGain; gains[0].sawtoothupGain = periodicGain; gains[1].sawtoothupGain = periodicGain; gains[0].sawtoothdownGain = periodicGain; gains[1].sawtoothdownGain = periodicGain; gains[0].damperGain = 0; //added always-active damping elsewhere gains[1].damperGain = 0; Joystick.setGains(gains); Serial1.begin(500000); delay(100); //clear the input buffer while(Serial1.available()>0){ Serial1.read(); } setOdriveState(0, 8); //AXIS_STATE_CLOSED_LOOP_CONTROL = 8 delay(100); setOdriveState(1, 8); delay(100); } void updateJoystickPos() { Joystick.setXAxis(pos[0]); Joystick.setYAxis(pos[1]); } void updateEffects(bool recalculate){ for (int i =0; i < 2; i++) { effects[i].frictionMaxPositionChange = 15; // TODO: find proper values for these automatically effects[i].inertiaMaxAcceleration = 5; effects[i].damperMaxVelocity = 50; } effects[0].springMaxPosition = maxX; effects[1].springMaxPosition = maxY; effects[0].springPosition = pos[0]; effects[1].springPosition = pos[1]; unsigned long currentMillis; currentMillis = millis(); int16_t diffTime = currentMillis - lastEffectsUpdate; if (diffTime > 0 && recalculate) { lastEffectsUpdate = currentMillis; int16_t positionChangeX = pos[0] - lastX; int16_t positionChangeY = pos[1] - lastY; int16_t velX = positionChangeX / diffTime; int16_t velY = positionChangeY / diffTime; int16_t accelX = ((velX - lastVelX) * 10) / diffTime; int16_t accelY = ((velY - lastVelY) * 10) / diffTime; effects[0].frictionPositionChange = velX; effects[1].frictionPositionChange = velY; effects[0].inertiaAcceleration = accelX; effects[1].inertiaAcceleration = accelY; effects[0].damperVelocity = velX; effects[1].damperVelocity = velY; lastX = pos[0]; lastY = pos[1]; lastVelX = velX; lastVelY = velY; lastAccelX = accelX; lastAccelY = accelY; } else { effects[0].frictionPositionChange = lastVelX; effects[1].frictionPositionChange = lastVelY; effects[0].inertiaAcceleration = lastAccelX; effects[1].inertiaAcceleration = lastAccelY; effects[0].damperVelocity = lastVelX; effects[1].damperVelocity = lastVelY; } Joystick.setEffectParams(effects); Joystick.getForce(forces); } Eventually I'd like to speed things up even further by just reading the position in as an int and avoid all the float math. -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
I cut the head off a m5x50 shoulder bolt, put it in the existing hole with a flange nut on the bottom and a shaft collar at the top. -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
Between adding always-active damping and dropping odrv0.axis0.motor.config.current_control_bandwidth from the default of 2000 down to 100 it seems like I solved my out of control oscillation problems at higher torques. Motors are nice and quiet too. Now the limiting factor really is the strength of the printed gimbal joint, and all that vibration has put that "clunk" back in to the roll axis, even with re-tightening everything down -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
Thank you for making it available. You absolutely saved me a lot of headache getting it figured out. I took a look at trying to run it on an SAME51 chip (basically an SAMD51 chip with a CAN bus - supposedly more reliable than the odrive serial uart) but the USB library stuff goes way over my head. I think my next step is adding some switches to the arduino to turn on/off motors as well as adding some damping that isnt dependent on it being enabled in-game. I also replaced the m5 bolt that runs up through the GIMBAL_CORE_JOINT with a proper 5mm shaft, since the bolt threads were slightly undersized for the bearing ID and caused a barely perceptible amount of slop. -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
I had a similar "clunk" when going from one direction of force to the other. Noticed the frames were flexing and allowing the bearings to slide in and out on the joints. Fixed by tapping the holes on the joints and securing the bearings that way. Clunk-1.webm -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
My previous arduino code is garbage and I am deleting it out of shame. Here is the much much better Fino implementation. Download the Fino library, put it in your ~/Documents/Arduino/libraries folder and replace the Fino.ino and joystick.ino files with the code below. I'm also filtering the input encoder position with a simple MovingAverage library - available through the library manager as MovingAverager by Ian Carey I have my DCS controls FF_Tune menu set as follows: Trimmer Force = 75 Shake Force = 5 Swap Axis = unchecked Invert X = unchecked Invert Y = unchecked Fino.ino #define DEBUGNO // the digits mean Mmmmrrr (M=Major,m=minor,r=revision) #define SKETCH_VERSION 3000001 #include "src/Joystick.h" #include "config.h" #include "order.h" #include <ODriveArduino.h> #include <ODriveEnums.h> #include <HardwareSerial.h> #include <MovingAverage.h> // Printing with stream operator helper functions template<class T> inline Print& operator <<(Print &obj, T arg) { obj.print(arg); return obj; } template<> inline Print& operator <<(Print &obj, float arg) { obj.print(arg, 4); return obj; } // ------------------------- // Various global variables // ------------------------- unsigned long lastEffectsUpdate; unsigned long nextJoystickMillis; unsigned long nextEffectsMillis; // -------------------------- // Joystick related variables // -------------------------- #define minX -10000 #define maxX 10000 #define minY -10000 #define maxY 10000 //forces[] has a range of +/-10000, so this sets a safe motor amperage float motorAmps = 1; float forceToAmps; //sets the values of all the periodic function gains float periodicGain = 0.05; bool is_connected = false; bool forces_requested = false; bool pos_updated = false; int16_t pos[2] = {0, 0}; int lastX; int lastY; int lastVelX; int lastVelY; int lastAccelX; int lastAccelY; float scaleFactorX; float scaleFactorY; //Input filters - the number is the # of readings its averageing, higher number more stable but slower, lower number less filtered but faster response int readX; MovingAverage filterX(4); int readY; MovingAverage filterY(4); //because odrive reports in motor rotations we have to do some converting //these are the limits and center for my joystick in odrive-reported-rotations //maxX is joystick to the right, maxY is joystick pushed away from you float maxXRot = 0.13; float minXRot = -0.11; float maxYRot = -0.23; float minYRot = 0.17; float centerXRot = 0.02; float centerYRot = -0.03; //Hardware serial port HardwareSerial& odrive_serial = Serial1; // ODrive object ODriveArduino odrive(odrive_serial); EffectParams effects[2]; int32_t forces[2] = {0, 0}; Joystick_ Joystick( JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_JOYSTICK, 0, 0, // Button Count, Hat Switch Count true, true, false, // X, Y, Z false, false, false, // Rx, Ry, Rz false, false); // rudder, throttle void setup() { //Makes uploading the new sketch easier, because serial can get flooded //if you have trouble hit the reset pin as it finishes compiling delay(2000); setupJoystick(); // setup communication Serial.begin(SERIAL_BAUD); // setup timing and run them as soon as possible lastEffectsUpdate = 0; nextJoystickMillis = 0; nextEffectsMillis = 0; } void loop(){ //get_messages_from_serial(); //i setup my odrive as motor 1 is x, 0 is y :bigbrain: readX = (int)((odrive.GetPosition(1)-centerXRot)*scaleFactorX); readY = (int)((odrive.GetPosition(0)-centerYRot)*scaleFactorY); //I'm sure this wont cause any issues in the future pos_updated = true; pos[0] = filterX.addSample(readX); pos[1] = filterY.addSample(readY); //Serial << "x" << pos[0] << ", y" << pos[1] << "\n"; unsigned long currentMillis; currentMillis = millis(); // do not run more frequently than these many milliseconds if (currentMillis >= nextJoystickMillis) { updateJoystickPos(); nextJoystickMillis = currentMillis + 2; // we calculate condition forces every 100ms or more frequently if we get position updates if (currentMillis >= nextEffectsMillis || pos_updated) { updateEffects(true); nextEffectsMillis = currentMillis + 100; pos_updated = false; } else { // calculate forces without recalculating condition forces // this helps having smoother spring/damper/friction // if our update rate matches our input device updateEffects(false); } //Again, i have mine set up as X axis is motor 1, Y axis is motor 0 odrive.SetCurrent(1,(float)forces[0]*forceToAmps); odrive.SetCurrent(0,(float)-forces[1]*forceToAmps); //Serial << "fx" << (forces[0]*forceToAmps) << ", fy" << (forces[1]*forceToAmps) << "\n"; } } joystick.ino void setupJoystick() { forceToAmps = motorAmps/10000; scaleFactorX = (2*maxX) / (maxXRot - minXRot); scaleFactorY = (2*maxY) / (maxYRot - minYRot); Joystick.setXAxisRange(minX, maxX); Joystick.setYAxisRange(minY, maxY); Joystick.begin(); Gains gains[FFB_AXIS_COUNT]; gains[0].springGain = 0.7; gains[1].springGain = 0.7; gains[0].frictionGain = 0.25; gains[1].frictionGain = 0.25; gains[0].squareGain = periodicGain; gains[1].squareGain = periodicGain; gains[0].sineGain = periodicGain; gains[1].sineGain = periodicGain; gains[0].triangleGain = periodicGain; gains[1].triangleGain = periodicGain; gains[0].sawtoothupGain = periodicGain; gains[1].sawtoothupGain = periodicGain; gains[0].sawtoothdownGain = periodicGain; gains[1].sawtoothdownGain = periodicGain; Joystick.setGains(gains); odrive_serial.begin(500000); //Serial.println("requesting torque control"); //sometimes it fails to turn on axis 0, so i dunno, try it twice? odrive.run_state(0, AXIS_STATE_CLOSED_LOOP_CONTROL, false); delay(100); odrive.run_state(1, AXIS_STATE_CLOSED_LOOP_CONTROL, false); delay(50); odrive.run_state(0, AXIS_STATE_CLOSED_LOOP_CONTROL, false); } void updateJoystickPos() { Joystick.setXAxis(pos[0]); Joystick.setYAxis(pos[1]); } void updateEffects(bool recalculate){ for (int i =0; i < 2; i++) { effects[i].frictionMaxPositionChange = 25; // TODO: find proper values for these automatically effects[i].inertiaMaxAcceleration = 10; effects[i].damperMaxVelocity = 40; } effects[0].springMaxPosition = maxX; effects[1].springMaxPosition = maxY; effects[0].springPosition = pos[0]; effects[1].springPosition = pos[1]; unsigned long currentMillis; currentMillis = millis(); int16_t diffTime = currentMillis - lastEffectsUpdate; if (diffTime > 0 && recalculate) { lastEffectsUpdate = currentMillis; int16_t positionChangeX = pos[0] - lastX; int16_t positionChangeY = pos[1] - lastY; int16_t velX = positionChangeX / diffTime; int16_t velY = positionChangeY / diffTime; int16_t accelX = ((velX - lastVelX) * 10) / diffTime; int16_t accelY = ((velY - lastVelY) * 10) / diffTime; effects[0].frictionPositionChange = velX; effects[1].frictionPositionChange = velY; effects[0].inertiaAcceleration = accelX; effects[1].inertiaAcceleration = accelY; effects[0].damperVelocity = velX; effects[1].damperVelocity = velY; #ifdef DEBUG write_order(LOG); Serial.print(currentMillis); Serial.print("X"); Serial.print(pos[0]); Serial.print("Y"); Serial.print(pos[1]); Serial.print("C"); Serial.print(positionChangeX); Serial.print(","); Serial.print(positionChangeY); Serial.print("V"); Serial.print(velX); Serial.print(","); Serial.print(velY); Serial.print("A"); Serial.print(accelX); Serial.print(","); Serial.print(accelY); #endif lastX = pos[0]; lastY = pos[1]; lastVelX = velX; lastVelY = velY; lastAccelX = accelX; lastAccelY = accelY; } else { effects[0].frictionPositionChange = lastVelX; effects[1].frictionPositionChange = lastVelY; effects[0].inertiaAcceleration = lastAccelX; effects[1].inertiaAcceleration = lastAccelY; effects[0].damperVelocity = lastVelX; effects[1].damperVelocity = lastVelY; } Joystick.setEffectParams(effects); Joystick.getForce(forces); #ifdef DEBUG if (diffTime > 0 && recalculate) { Serial.print("F"); Serial.print(forces[0]); Serial.print(","); Serial.println(forces[1]); } #endif } If I turn the gains up too high it'll start oscillating something fierce, but these seem alright for my setup. Maybe direct-driving the gimbals would take out that small springiness of the belt and allow for higher gains? -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
Sorry, but the comms issues I was having was just with the serial output to the arduino. My SPI comms seem to be solid. With the motors turned on the odrive would just stop responding to GetPosition() calls after a couple seconds. It would take longer at slower baudrates but it would eventually happen. The strange thing was the SetPosition() commands going the other way would get through just fine, even when the odrive stopped responding to position requests. Do you have ferrite toroids on the motor leads, shielding wrap on your motor wires and encoder wires, grounding said shielding wrap to the V- input pad? Also just routing the encoder cables away from the motors and motor leads. A word of warning on the Due - the ArduinoJoystickWithFFBLibrary and Fino are built around the usb comms of an ATmega32u4 chip. I spent a little while trying to see if I could run it on a SAMD51 chip, and couldn't figure it out. I've determined that the jitter is the encoder data, definitely not a commanded vibration input. With friction turned on it gets pretty horrible with how it's trying to apply friction forces to the constantly changing position. I'm going to take a look at redoing my encoder/magnet mounts to see if I can reduce some jitter that way. With how much I've turned down my motors I could probably eliminate the belts and just direct-drive the gimbal, so I'll figure out how best to mount everything for that. I figure the loss of encoder resolution will be worth it. -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
Be warned, it's still very much in the kludged-together state. I'm thinking of trying out the Fino library instead, since he's got nice rate control and the friction position change/velocity calculations set up already. It's a bit jittery in DCS, and I'm not sure quite sure how much of that is forcefeedback vibration or jittery encoder vibration. My problems with the Odrive UART not responding to the GetPosition() calls were solved by flashing the older 0.5.1 firmware. I also downgraded the odrivetool command line to keep things compatible. BAD CODE USED TO BE HERE, REMOVED TO SAVE THE WORLD FROM MY INEPTITUDE LOOK AT A LATER POST BELOW FOR BETTER CODE I included the Ewma library for input filtering, which is available to install through the arduino library manager window. For the ArduinoJoystickWithFFBLibrary and the OdriveArduino library you'll have to download them separately and move them into your ~/Documents/Arduino/libraries folder. And these are the commands for setting up the odrive parameters with the as5048a encoders: //Initial Setup odrv0.axis0.motor.config.resistance_calib_max_voltage = 4 odrv0.axis0.encoder.config.abs_spi_cs_gpio_pin = 6 odrv0.axis0.encoder.config.mode = ENCODER_MODE_SPI_ABS_AMS odrv0.axis0.encoder.config.cpr = 2**14 odrv0.axis1.motor.config.resistance_calib_max_voltage = 4 odrv0.axis1.encoder.config.abs_spi_cs_gpio_pin = 7 odrv0.axis1.encoder.config.mode = ENCODER_MODE_SPI_ABS_AMS odrv0.axis1.encoder.config.cpr = 2**14 odrv0.config.uart_baudrate = 500000 ...with the 0.5.1 firmware the brake resistor is just assumed to be connected ...for the newer firmwares you'll need to configure it odrv0.config.enable_brake_resistor = True ...I set the current limits to 6, I dont really need more with my motors odrv0.axis0.motor.config.current_lim = 6 odrv0.axis1.motor.config.current_lim = 6 ...raise the velocity limits to 6rev/s, you can pretty easily hit the 2rev/s default odrv0.axis0.controller.config.vel_limit = 6 odrv0.axis1.controller.config.vel_limit = 6 odrv0.save_configuration() odrv0.reboot() //Motor and Encoder Calibration //do these with the motors free to spin, not hooked up to the gimbal odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE odrv0.axis0.motor.config.pre_calibrated = True odrv0.axis0.encoder.config.pre_calibrated = True odrv0.axis1.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE odrv0.axis1.motor.config.pre_calibrated = True odrv0.axis1.encoder.config.pre_calibrated = True odrv0.save_configuration() odrv0.reboot() odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL odrv0.axis1.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL ...this should turn on the motors and have them hold position where they are ...try turning the motors by hand and see if they push back ...if motors aren't on check for errors by running: dump_errors(odrv0) ...you can follow the Tuning section to tune the gains: https://docs.odriverobotics.com/control.html ...but it might be better to do that with the belts connected //Anticogging Calibration //do these with the motors free to spin, not hooked up to the gimbal odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL odrv0.axis0.controller.start_anticogging_calibration() odrv0.axis1.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL odrv0.axis1.controller.start_anticogging_calibration() ...wait for anticogging to finish, it can take a while like 10 minutes ...you can increase pos_gain and vel_integrator_gain by a factor of 10 to speed it up, but remember to set them back ...you can check if anticogging calibration is finished by running: odrv0.axis0.controller.config.anticogging.calib_anticogging ...and if it returns True, calibration is still running, it will return False when its complete odrv0.axis0.controller.config.anticogging.anticogging_enabled = True odrv0.axis1.controller.config.anticogging.anticogging_enabled = True odrv0.save_configuration() odrv0.reboot() -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
Downgrading the odrive firmware to 0.5.1 fixed all my serial dropout problems. I kludged together some code using this library on an arduino pro micro protip: spring effects in that library expect you to set your joystick range as (-min,max), not (0,max) like it shows in the example [at least to make effects in DCS work] I've spent about 30 minutes just making sure it works right and getting gains in the right neighborhood to where they won't break the gimbal. I'll get my setup mounted properly and clean up the code in the next couple days and post some more. -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
I'm using the development boards, which each came with a small round magnet (AS5000-MD6H-2 diametric magnet). I printed the encoder mounts such that the surface of the chip would be 1.5mm away from the magnet, since the datasheet recommends a 0.5-2.5mm range. No luck with shielding and ferrite rings fixing my odrive->arduino communication problem. Posting about it here in the odrive forums. @propeler let me know if you need any testers for the odrive firmware -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
Correct, I'm using the as5048a's. While using incremental encoders is probably do-able, its a lot simpler to have the odrive already know where it is at startup without needing to do an index search. Remember that the joystick has limited travel and the motors can't make a full rotation. With the designed gear ratio the motors have a lock-to-lock range of ~half a rotation total (+/- 0.25 rotations each side). With a stick extension I'm only going to be using about half of that available travel on the roll axis, so the 2048ppr resolution of the amt102's might start becoming noticeable. -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
I followed propeler's design specs so I wouldn't have to recalculate bend radii. Cheap and cheerful A36 mild steel @ 3mm. With the slots there's some wiggle room, but my holes are lined up basically in the middle of the slots. Eventually I may need to add some sort of rust preventative though. The thickness is needed for the tapped holes mounting the main gimbal frame bearings. As designed there's no space for hex nuts there. If you lengthened the shoulder on the arm frames and spaced out the sheet metal plates and mounting holes accordingly you could get away with using thinner steel that you could bend in your garage. Arduino -> oDrive serial connection keeps dropping due to noise, and Amazon has lost two separate ferrite ring orders. Why did I have to forget those on my digikey order? I've been mourning the loss of Radioshack and Fry's recently. -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
Update to my build. Had the metal plates cut and bent by SendCutSend. I spent an afternoon deburring edges and tapping holes. The holes came slightly undersized and needed a pass with a drill before they were tapped. I'm running the odrive off a 24v-350w power supply that currently runs my bass shakers. Motors are limited to 8A as the gimbal made some unnerving creaking sounds at 10A. Might need to get another psu since both motors at 8A will pull 380W and leave no headroom for the shakers. Had some trouble with noise on the SPI lines disrupting the encoders, but rewired with shielded cables and they now work with the motors at "full" draw. Still waiting on ferrite rings for the motor leads which would help. I'm very impressed with the sensitivity of the encoders. They react to the slightest pressure on the stick with no slop. Currently my plan for the programming side is to run the odrive in torque mode and control it with a teensy board running this FFB library. (apparently the usb libraries used by the teensy are not the same as the standard arduino libraries and therefore not compatible with the ffb library without serious effort, so now I'm waiting on a arduino pro micro) Big thanks to propeler for all the work developing the design and making it freely available. -
Honey, I developed FFB joystick (DIY)
theperson replied to propeler's topic in PC Hardware and Related Software
I'm starting a build of one of these using a resin printer. Using siraya blu/tenacious mix. The frame arm needed some small modifications to get it to print. Once I have it all together and I trust the strength/dimensions of the print I'll post my modified/supported stls. Planning on using an odrive, 6354-60kv motors with as5048a encoders.