wii摄像头的前世今生 - 可打印的版本 +- LightGunDiyer 光枪爱好者 (http://www.wukongxuetang.com/bbs) +-- 版块: 光枪原理与光枪DIY (http://www.wukongxuetang.com/bbs/forumdisplay.php?fid=1) +--- 版块: 光枪-传感器 (http://www.wukongxuetang.com/bbs/forumdisplay.php?fid=2) +--- 主题: wii摄像头的前世今生 (/showthread.php?tid=3) |
wii摄像头的前世今生 - wukong - 03-21-2024 一、wii摄像头的前世 Wii U是日本任天堂公司2006年11月19日所推出的家用游戏主机,在该游戏机外设的手持控制棒Wii Remote里面有一个捕捉当前用户动作的摄像头。 Wii U于2012年11月在全球发售,因为销售不及预期,在2017年被任天堂公司宣布停产,共计销售不到1400万台。任天堂最失败的主机项目以失败而告终,成为了游戏业内的一个经典案例。 二、wii手柄协议的破解与利用 数据来源:http://johnnylee.net/projects/wii/ wii摄像头的首次使用能查到来源的就是这个李胖子(Johnny Chung Lee),让wii摄像头一战成名,并凭借该发现和应用,胖子李去了微软。没错,去了微软。 由于胖子李的突出贡献,wii摄像头的首次大规模被使用的场景并不是光枪,而是电子白板。台湾网友至今仍有使用并乐此不疲。 [attachment=1]
三、wii手柄摄像头参数 该数据来源:https://wiibrew.org/wiki/Wiimote The Wii Remote includes a 128x96 monochrome camera with built-in image processing. The camera looks through an infrared pass filter in the remote's plastic casing. The camera's built-in image processing is capable of tracking up to 4 moving objects, and these data are the only data available to the host. Raw pixel data is not available to the host, so the camera cannot be used to take a conventional picture. The built-in processor uses 8x subpixel analysis to provide 1024x768 resolution for the tracked points. The Sensor Bar that comes with the Wii includes two IR LED clusters at each end, which are tracked by the Wii Remote to provide pointing information. The distance between the centers of the LED clusters is 20 cm (as measured on one unit). The IR camera has an effective field of view is about 33 degrees horizontally and 23 degrees vertically (as measured on one unit). With the IR-pass filter intact, 940nm sources are detected with approximately twice the intensity of equivalent 850nm sources, but are not resolved as well at close distances. If the filter is removed, it can track any bright object. However, the IR filter referred to here is not only the dark plastic window of the wiimote but also a teensy slab of dichroic-coated glass inside the camera module. One may operate the wiimote having installed neither, one or the other, or both filters. 数据参数汇总后: wii摄像头分辨率:1024*768 wii摄像头接口: IIC 25MHZ晶振驱动 wii摄像头角度:水平33度 垂直23度 wii摄像头光源:940nm 四、wii摄像头pcb电路图 五、wii摄像头arduino库 wiicam.h #ifndef wiiCam_H #define wiiCam_H #include "Arduino.h" /** * IR point data structure */ struct irPoint { bool valid; uint8_t invalidCount; double x; uint16_t xRaw; double y; uint16_t yRaw; float avgBrightness; uint8_t avgBrightnessRaw; float maxBrightness; uint8_t maxBrightnessRaw; uint16_t area; uint8_t range; uint8_t radius; uint8_t boundaryLeft; uint8_t boundaryRight; uint8_t boundaryUp; uint8_t boundaryDown; uint8_t aspectRatio; uint8_t Vx; uint8_t Vy; }; struct avgPoint { double x; double y; float avgBrightness; float maxBrightness; }; class wiiCam { public: wiiCam(uint8_t sda, uint8_t scl); void initialize(); void setSensitivity(uint8_t val); float getGain(); void setGain(float gainTemp); uint8_t getPixelBrightnessThreshold(); void setPixelBrightnessThreshold(uint8_t value); bool getOutput(uint8_t format); void getMeasuredPoint(uint8_t point); void setFramePeriod(float framePeriod); float getFramePeriod(); uint8_t getObjectNumberSetting(); void setObjectNumberSetting(uint8_t val); void setAverageCount(uint8_t averageCount); uint8_t getAverageCount(); void setMirrorX(bool en); bool getMirrorX(); void setMirrorY(bool en); bool getMirrorY(); void setRotation(bool en); bool getRotation(); void setOffset(int16_t x, int16_t y); void setOffsetX(int16_t x); void setOffsetY(int16_t y); int16_t getOffsetX(); int16_t getOffsetY(); void setScale(float scaleX, float scaleY); void setScaleX(float scale); void setScaleY(float scale); float getScaleX(); float getScaleY(); void setCalibrationEnable(bool en); bool getCalibrationEnable(); int getCalibrationArray(int point, int axis); void setCalibrationArray(int point, int axis, int value); void orderCalibrationArray(); void calculateHomographyMatrix(); void setCalibrationOffsetEnable(bool en); bool getCalibrationOffsetEnable(); int getCalibrationOffsetArray(int point, int axis); void setCalibrationOffsetArray(int point, int axis, int value); void orderCalibrationOffsetArray(); void calculateOffsetHomographyMatrix(); irPoint irPoints[16]; uint8_t detectedPoints; private: uint8_t readRegister(uint8_t reg); void writeRegister(uint8_t reg,uint8_t val); uint8_t sensitivityBlock1[9]; uint8_t sensitivityBlock2[2]; float framePeriod = 1; unsigned long exposureTime; float gain; uint8_t outputBuffer[256]; uint8_t outputFormat; uint8_t objectNumber = 4; uint8_t _sda; uint8_t _scl; avgPoint _avgPoints[16]; uint8_t _avgCounter; uint8_t _avgCount = 1; bool _calEn = false; bool _calOffsetEn = false; bool _mirrorX = false; bool _mirrorY = false; bool _rotation = false; int16_t _offsetX = 0; int16_t _offsetY = 0; float _scaleX = 1; float _scaleY = 1; }; #endif /* wiiCam_H */ wiicam.cpp #include "wiiCam.h" #include "Arduino.h" #include <Wire.h> #include "homography.h" homography cal2; homography offset2; //I2C address #define IR_ADDRESS 0x58 //I2C message size #define MSGSIZE 40 wiiCam::wiiCam(uint8_t sda, uint8_t scl){ _sda = sda; _scl = scl; Wire.begin(_sda,_scl,400000); for (int i=0; i<9; i++) sensitivityBlock1[i] = 0; for (int i=0; i<2; i++) sensitivityBlock2[i] = 0; } void wiiCam::initialize(){ Wire.beginTransmission(IR_ADDRESS); Wire.write(0x30); Wire.write(0x01); Wire.endTransmission(); delay(10); setSensitivity(4); Wire.beginTransmission(IR_ADDRESS); Wire.write(0x33); Wire.write(0x05); Wire.endTransmission(); delay(10); } uint8_t wiiCam::readRegister(uint8_t reg){ Wire.beginTransmission(IR_ADDRESS); Wire.write(reg); Wire.endTransmission(); Wire.requestFrom(IR_ADDRESS, 1); return Wire.read(); } /* * Write to the sensor register */ void wiiCam::writeRegister(uint8_t reg,uint8_t val){ Wire.beginTransmission(IR_ADDRESS); Wire.write(reg); Wire.write(val); Wire.endTransmission(); } void wiiCam::setSensitivity(uint8_t val) { uint8_t sens = (uint8_t)val; uint8_t p0, p1, p2, p3; if (sens == 0) { p0 = 0x64; p1 = 0xFE; p2 = 0xFD; p3 = 0x05; } else if (sens == 1) { p0 = 0x96; p1 = 0xB4; p2 = 0xB3; p3 = 0x04; } else if (sens == 2) { p0 = 0xAA; p1 = 0x64; p2 = 0x63; p3 = 0x03; } else if (sens == 3) { p0 = 0xC8; p1 = 0x36; p2 = 0x35; p3 = 0x03; } else if (sens == 4) { p0 = 0x72; p1 = 0x20; p2 = 0x1F; p3 = 0x03; } p0 = 0xFF; sensitivityBlock1[0] = 0x02; sensitivityBlock1[1] = 0x00; sensitivityBlock1[2] = 0x00; sensitivityBlock1[3] = 0x71; sensitivityBlock1[4] = 0x01; sensitivityBlock1[5] = 0x00; sensitivityBlock1[6] = p0; //max intensity threshold sensitivityBlock1[7] = 0x00; sensitivityBlock1[8] = p1; //brightness sensitivityBlock2[0] = p2; //max brightness REG 0x1A sensitivityBlock2[1] = p3; //min intensity threshold REG 0x1B Wire.beginTransmission(IR_ADDRESS); Wire.write(0x00); for (int i=0; i<9; i++) Wire.write(sensitivityBlock1[i]); Wire.endTransmission(); delay(10); Wire.beginTransmission(IR_ADDRESS); Wire.write(0x1A); Wire.write(sensitivityBlock2[0]); Wire.write(sensitivityBlock2[1]); Wire.endTransmission(); delay(10); Wire.beginTransmission(IR_ADDRESS); Wire.write(0x30); Wire.write(0x08); Wire.endTransmission(); delay(10); } float wiiCam::getGain(){ uint8_t reg = readRegister(0x08); float gain = 0.01*round((255 - readRegister(0x08))/0.3657143) + 1; return gain; } void wiiCam::setGain(float gainTemp){ uint8_t val = round(255-(gainTemp-1)*36.57143); if ((uint8_t)val == 1) val = 0; writeRegister(0x08,(uint8_t)val); writeRegister(0x1A,(uint8_t)val-1); } uint8_t wiiCam::getPixelBrightnessThreshold(){ return readRegister(0x1B); } void wiiCam::setPixelBrightnessThreshold(uint8_t value){ writeRegister(0x1B,value); } bool wiiCam::getOutput(uint8_t format){ /** * Set format * Format 1: * Addr: 0x36 * 10 bytes * X: 0-1023 * Y: 0-767 * * Format 2: * Addr: 0x33 * 12 bytes * X: 0-1023 * Y: 0-767 * Area: 4 bits * * Format 3: * Addr: 0x36 * 18 bytes * X: 0-1023 * Y: 0-767 * Area: 3 bits * Xmin: 7 bits * Xmax: 7 bits * Ymin: 7 bits * Ymax: 7 bits * Intensity: 8 bits */ //IR sensor read Wire.beginTransmission(IR_ADDRESS); Wire.write(0x36); Wire.endTransmission(); // Request the 2 byte heading (MSB comes first) Wire.requestFrom(IR_ADDRESS, MSGSIZE); int i=0; while(Wire.available() && i < MSGSIZE) { outputBuffer[i] = Wire.read(); i++; } detectedPoints = 0; _avgCounter++; bool newPoints = false; for (int i=0; i<objectNumber; i++){ getMeasuredPoint(i); } if (_avgCounter >= _avgCount) newPoints = true; if (detectedPoints == 0 || _avgCounter >= _avgCount) { _avgCounter = 0; } return newPoints; } void wiiCam::getMeasuredPoint(uint8_t point){ uint16_t xCoord,yCoord; uint8_t area,maxBrightness, avgBrightness; xCoord = ((outputBuffer[3+point*9]>>4)&3) << 8 | outputBuffer[1+point*9]; yCoord = ((outputBuffer[3+point*9]>>6)&3) << 8 | outputBuffer[2+point*9]; area = outputBuffer[3+point*9]&&15; maxBrightness = outputBuffer[9+point*9]; avgBrightness = maxBrightness; xCoord *= 4; yCoord *= 5.33; //point not detected if (maxBrightness == 255) { if (irPoints[point].invalidCount < 255) irPoints[point].invalidCount++; if (irPoints[point].invalidCount > 5) { irPoints[point].valid = false; irPoints[point].x = 0; irPoints[point].y = 0; irPoints[point].avgBrightness = 0; irPoints[point].maxBrightness = 0; _avgPoints[point].x += _avgPoints[point].x/_avgCounter; _avgPoints[point].y += _avgPoints[point].y/_avgCounter; _avgPoints[point].avgBrightness += _avgPoints[point].avgBrightness/_avgCounter; _avgPoints[point].maxBrightness += _avgPoints[point].maxBrightness/_avgCounter; return; } } else { irPoints[point].valid = true; irPoints[point].invalidCount = 0; _avgPoints[point].x += xCoord; _avgPoints[point].y += yCoord; _avgPoints[point].avgBrightness += avgBrightness; _avgPoints[point].maxBrightness += maxBrightness; if (_avgCounter >= _avgCount) { double x = _avgPoints[point].x/_avgCounter; double y = _avgPoints[point].y/_avgCounter; //If calibration is enabled, perform homography transform if (_calEn) { cal2.calculateCoordinates(x, y); x = cal2.getX(); y = cal2.getY(); //If calibration offset is enabled, perform homography transform if (_calOffsetEn) { offset2.calculateCoordinates(x, y); x = offset2.getX(); y = offset2.getY(); } } //If rotation is enabled, flip x and y if (_rotation) { double xTemp = x; x = y; y = xTemp; } //If mirrorX is enabled if (_mirrorX) x = 4095 - x; //If mirrorY is enabled if (_mirrorY) y = 4095 - y; //Add offset x += _offsetX; y += _offsetY; //Scale coordinates x = (x-2048)*_scaleX+2048; y = (y-2048)*_scaleY+2048; //Store the values irPoints[point].x = x; irPoints[point].y = y; irPoints[point].avgBrightness = _avgPoints[point].avgBrightness/_avgCounter; irPoints[point].maxBrightness = _avgPoints[point].maxBrightness/_avgCounter; //Reset _avgPoints _avgPoints[point].x = 0; _avgPoints[point].y = 0; _avgPoints[point].avgBrightness = 0; _avgPoints[point].maxBrightness = 0; } } detectedPoints++; //Store all the values irPoints[point].xRaw = xCoord; irPoints[point].yRaw = yCoord; irPoints[point].avgBrightnessRaw = avgBrightness; irPoints[point].maxBrightnessRaw = maxBrightness; irPoints[point].area = area; //irPoints[point].range = range; //irPoints[point].radius = radius; //irPoints[point].boundaryLeft = boundaryLeft; //irPoints[point].boundaryRight = boundaryRight; //irPoints[point].boundaryUp = boundaryUp; //irPoints[point].boundaryDown = boundaryDown; //irPoints[point].aspectRatio = aspectRatio; //irPoints[point].Vx = Vx; //irPoints[point].Vy = Vy; } void wiiCam::setFramePeriod(float fPeriod) { if (fPeriod < 15) fPeriod = 15; framePeriod = fPeriod; } float wiiCam::getFramePeriod() { return framePeriod; } uint8_t wiiCam::getObjectNumberSetting() { return objectNumber; } void wiiCam::setObjectNumberSetting(uint8_t val) { if (val > 4) val = 4; objectNumber = val; } void wiiCam::setAverageCount(uint8_t averageCount) { if (averageCount < 1) averageCount = 1; _avgCount = averageCount; } uint8_t wiiCam::getAverageCount() { return _avgCount; } void wiiCam::setMirrorX(bool en){ _mirrorX = en; } bool wiiCam::getMirrorX(){ return _mirrorX; } void wiiCam::setMirrorY(bool en){ _mirrorY = en; } bool wiiCam::getMirrorY(){ return _mirrorY; } void wiiCam::setRotation(bool en){ _rotation = en; } bool wiiCam::getRotation(){ return _rotation; } void wiiCam::setOffset(int16_t x, int16_t y){ _offsetX = x; _offsetY = y; } void wiiCam::setOffsetX(int16_t x){ _offsetX = x; } void wiiCam::setOffsetY(int16_t y){ _offsetY = y; } int16_t wiiCam::getOffsetX(){ return _offsetX; } int16_t wiiCam::getOffsetY(){ return _offsetY; } void wiiCam::setScale(float scaleX, float scaleY) { _scaleX = scaleX; _scaleY = scaleY; } void wiiCam::setScaleX(float scale) { _scaleX = scale; } void wiiCam::setScaleY(float scale) { _scaleY = scale; } float wiiCam::getScaleX() { return _scaleX; } float wiiCam::getScaleY() { return _scaleY; } void wiiCam::setCalibrationEnable(bool en){ _calEn = en; } bool wiiCam::getCalibrationEnable(){ return _calEn; } int wiiCam::getCalibrationArray(int point, int axis) { return cal2.getCalArray(point, axis); } void wiiCam::setCalibrationArray(int point,int axis, int value) { cal2.setCalArray(point, axis, value); } void wiiCam::orderCalibrationArray() { cal2.orderCalArray(); } void wiiCam::calculateHomographyMatrix() { cal2.calculateHomographyMatrix(); } void wiiCam::setCalibrationOffsetEnable(bool en){ _calOffsetEn = en; } bool wiiCam::getCalibrationOffsetEnable(){ return _calOffsetEn; } int wiiCam::getCalibrationOffsetArray(int point, int axis) { return offset2.getCalArray(point, axis); } void wiiCam::setCalibrationOffsetArray(int point,int axis, int value) { offset2.setCalArray(point, axis, value); } void wiiCam::orderCalibrationOffsetArray() { offset2.orderCalArray(); } void wiiCam::calculateOffsetHomographyMatrix() { offset2.calculateHomographyMatrix(); } RE: wii摄像头的前世今生 - luojingshen - 07-29-2024 这个PCB板的设计图,有电路图么? |