2016年5月31日 星期二

滾珠的演算法



line : 690 

function solve_board(board, step_callback, finish_callback) {
    var solutions = new Array(ROWS * COLS);
    var weights = get_weights();

    var seed_solution = make_solution(board);
    in_place_evaluate_solution(seed_solution, weights);

    for (var i = 0, s = 0; i < ROWS; ++ i) {
        for (var j = 0; j < COLS; ++ j, ++ s) {
            solutions[s] = copy_solution_with_cursor(seed_solution, i, j);
        }
    }

    var solve_state = {
        step_callback: step_callback,
        finish_callback: finish_callback,
        max_length: get_max_path_length(),
        dir_step: is_8_dir_movement_supported() ? 1 : 2,
        p: 0,
        solutions: solutions,
        weights: weights,
    };

    solve_board_step(solve_state);
}


function copy_solution_with_cursor(solution, i, j, init_cursor) {
    return {board: copy_board(solution.board),
            cursor: make_rc(i, j),
            init_cursor: init_cursor || make_rc(i, j),
            path: solution.path.slice(),
            is_done: solution.is_done,
            weight: 0,
            matches: []};
}





2016年5月29日 星期日

四元數




Ref : http://chiduino.blogspot.tw/2016/05/mpu6050_26.html?view=classic

這個是慣導的議題,基本上用這sensor無解。而且內容是錯的,還錯的離譜

張教授您好! 我剛看了這篇
https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-696.pdf
覺得應該比較有可參考性(比起各種網頁和國內外維基百科) ,但是內容似乎都是一樣的,都是以陀螺儀和加速計當sensor和四元數算出xyz的加速度後去除重力值然後積分兩次... 

數學上沒錯,講慣導是錯的。看你從哪裡切入。因為你講6050的使用,我認為你是做慣導,所以是錯的

如果你對於在 blogspot 裡要用 latex 顯示方程式有卡關的話,可以看這個: http://codebeta.blogspot.tw/....../blogspot-blogger......

https://www.youtube.com/watch?v=htoBvSq8jLA

姑且不論角加速度耦合,用一維描述就是錯的,你確定你移動時,sensor坐標系跟慣性坐標系重合得很好

其實有人已經將這些技術整合成模組了,例如這家大陸的TSENS號稱100m移動誤差<1m,我也很好奇是不是真的,但還拿不到產品
https://read01.com/ANna3m.html


另外還有一家XSENS也在做類似的慣性3D tracking,可以上去看看他們的產品
https://www.xsens.com/


還有可以參考相關的專利內容有提到演算法
http://www.google.com/patents/CN104897155A?cl=zh
嗯..其實慣性導航是個被研究很久的領域了

除非很有興趣不然重新發明輪子會有點麻煩囉


裡面內容節錄一下:
(2)獲取電磁定位信號並求解空間三維定位信息;
(3)依據行人運動檢測約束條件進行靜止檢測;


真的亮點是這兩個,所以它是有能力拿到絕對定位信號。然後他們有針對人體運動對速度作約束,基本上速度不發散,位置也不太會短時間爆走。


這個影片裡利用加速度計去得到位置的運算,效果看起來超準超好的! 說是x-IMU?

https://www.youtube.com/watch?v=6ijArKE8vKU

網站的AHRS解算的代碼,大陸幾乎都抄他的來用。
論文跟程式碼在這裡
http://www.x-io.co.uk/open-source-ahrs-with-x-imu/

噹噹,開源版的代碼在這裡,資料是傳回Matlab後處理。
https://github.com/....../Gait%20Tracking%20With%20x-IMU

推坑一下,這本書很經典。做INS必看。
https://books.google.com.tw/books?id=WwrCrn54n5cC......

2016年5月28日 星期六



#!/usr/bin/python3
import time
import serial # sudo apt-get install python3-serial
import sys

argc = len(sys.argv)

if argc < 3:
    print("Usage: " + sys.argv[0] + " <com_port> <gcode_file>")
    sys.exit()

print("Open COM port: " + sys.argv[1])
# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial(
    port = sys.argv[1],
    baudrate = 115200,
    parity = serial.PARITY_NONE,
    stopbits = serial.STOPBITS_ONE,
    bytesize = serial.EIGHTBITS,
    timeout = None,
    rtscts = None,
    dsrdtr = None
)
# wait for welcome message and flush it
# when open device node, cnc controller will reset and print message
time.sleep(1)
ser.flushInput()

# load ngc file
print("Open Gcode file: " + sys.argv[2])
f = open(sys.argv[2])
lines = f.readlines()
f.close()

# feed g codes line by line
for line in lines:
    if line[0] == ';':
        # drop comment
        continue;
    line_for_print = line.strip()
    print("Send: " + line_for_print)
    ser.write(str.encode(line))
    line_bytes = bytearray()
    while 1:
        c = ser.read()
        if c == b'\n':
            print("Recv: " + line_bytes.decode("utf-8"))
            break
        elif c >= b' ':
            # drop other control character such as 0xD
            line_bytes += c

ser.close()

Reference : http://wukcsoft.blogspot.tw/2014/12/cnc-if.html

accelstep 的研究筆記



 for (int i = 0; i < steps; i++)
  {
   drive_motor(stepperBase, -1);
  }

void drive_motor(AccelStepper motor, int steps)
{
  motor.move(steps);
  while (has_steps(motor))
  {
    motor.run();
  }
}

boolean has_steps(AccelStepper motor)
{
  return motor.distanceToGo() != 0;
}

long AccelStepper::distanceToGo()
{
    return _targetPos - _currentPos;
}


首先先 call   motor.move(steps);

void AccelStepper::move(long relative)
{
    moveTo(_currentPos + relative);
}

再來看   moveTo()

void AccelStepper::moveTo(long absolute)
{
    if (_targetPos != absolute)
    {
_targetPos = absolute;
computeNewSpeed();

    }
}

重點就是把  _targetPos = absolute;
然後 call  computeNewSpeed()


void AccelStepper::computeNewSpeed()
{
    long distanceTo = distanceToGo(); // +ve is clockwise from curent location

    long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16

    if (distanceTo == 0 && stepsToStop <= 1)
    {
// We are at the target and its time to stop
_stepInterval = 0;
_speed = 0.0;
_n = 0;
return;
    }

    if (distanceTo > 0)
    {
// We are anticlockwise from the target
// Need to go clockwise from here, maybe decelerate now
if (_n > 0)
{
   // Currently accelerating, need to decel now? Or maybe going the wrong way?
   if ((stepsToStop >= distanceTo) || _direction == DIRECTION_CCW)
_n = -stepsToStop; // Start deceleration
}
else if (_n < 0)
{
   // Currently decelerating, need to accel again?
   if ((stepsToStop < distanceTo) && _direction == DIRECTION_CW)
_n = -_n; // Start accceleration
}
    }
    else if (distanceTo < 0)
    {
// We are clockwise from the target
// Need to go anticlockwise from here, maybe decelerate
if (_n > 0)
{
   // Currently accelerating, need to decel now? Or maybe going the wrong way?
   if ((stepsToStop >= -distanceTo) || _direction == DIRECTION_CW)
_n = -stepsToStop; // Start deceleration
}
else if (_n < 0)
{
   // Currently decelerating, need to accel again?
   if ((stepsToStop < -distanceTo) && _direction == DIRECTION_CCW)
_n = -_n; // Start accceleration
}
    }

    // Need to accelerate or decelerate
    if (_n == 0)
    {
// First step from stopped
_cn = _c0;
_direction = (distanceTo > 0) ? DIRECTION_CW : DIRECTION_CCW;
    }
    else
    {
// Subsequent step. Works for accel (n is +_ve) and decel (n is -ve).
_cn = _cn - ((2.0 * _cn) / ((4.0 * _n) + 1)); // Equation 13
_cn = max(_cn, _cmin);
    }
    _n++;
    _stepInterval = _cn;
    _speed = 1000000.0 / _cn;
    if (_direction == DIRECTION_CCW)
_speed = -_speed;

#if 0
    Serial.println(_speed);
    Serial.println(_acceleration);
    Serial.println(_cn);
    Serial.println(_c0);
    Serial.println(_n);
    Serial.println(_stepInterval);
    Serial.println(distanceTo);
    Serial.println(stepsToStop);
    Serial.println("-----");
#endif
}

boolean AccelStepper::run()
{
    if (runSpeed())
computeNewSpeed();
    return _speed != 0.0 || distanceToGo() != 0;
}

run_speed() 如下:

當呼叫 runSpeed() ...會根據你的 旋轉方向去 增加或減少   _currentPos



boolean AccelStepper::runSpeed()
{
    // Dont do anything unless we actually have a step interval
    if (!_stepInterval)
return false;

    unsigned long time = micros();
    // Gymnastics to detect wrapping of either the nextStepTime and/or the current time
    unsigned long nextStepTime = _lastStepTime + _stepInterval;
    if (   ((nextStepTime >= _lastStepTime) && ((time >= nextStepTime) || (time < _lastStepTime)))
|| ((nextStepTime < _lastStepTime) && ((time >= nextStepTime) && (time < _lastStepTime))))

    {
if (_direction == DIRECTION_CW)
{
   // Clockwise
   _currentPos += 1;
}
else
{
   // Anticlockwise
   _currentPos -= 1;
}
step(_currentPos);

_lastStepTime = time;
return true;
    }
    else
    {
return false;
    }
}


重點就是  step(_currentPos)....

根據一開始的建構子決定的 _interface 來決定要用那一個 step ()..目前是取用  step1()....

void AccelStepper::step(long step)
{
    switch (_interface)
    {
        case FUNCTION:
            step0(step);
            break;

case DRIVER:
   step1(step);
   break;
 
case FULL2WIRE:
   step2(step);
   break;
 
case FULL3WIRE:
   step3(step);
   break;

case FULL4WIRE:
   step4(step);
   break;

case HALF3WIRE:
   step6(step);
   break;

case HALF4WIRE:
   step8(step);
   break;
    }
}

grbl hardware limit

$21 - Hard limits, bool    首先要啟動  $21...

Hard limit work basically the same as soft limits, but use physical switches instead. Basically you wire up some switches (mechanical, magnetic, or optical) near the end of travel of each axes, or where ever you feel that there might be trouble if your program moves too far to where it shouldn't. When the switch triggers, it will immediately halt all motion, shutdown the coolant and spindle (if connected), and go into alarm mode, which forces you to check your machine and reset everything.
To use hard limits with Grbl, the limit pins are held high with an internal pull-up resistor, so all you have to do is wire in a normally-open switch with the pin and ground and enable hard limits with$21=1. (Disable with $21=0.) We strongly advise taking electric interference prevention measures. If you want a limit for both ends of travel of one axes, just wire in two switches in parallel with the pin and ground, so if either one of them trips, it triggers the hard limit.
Keep in mind, that a hard limit event is considered to be critical event, where steppers immediately stop and will have likely have lost steps. Grbl doesn't have any feedback on position, so it can't guarantee it has any idea where it is. So, if a hard limit is triggered, Grbl will go into an infinite loop ALARM mode, giving you a chance to check your machine and forcing you to reset Grbl. Remember it's a purely a safety feature.

$5 - Limit pins invert, bool  ..  設為1代表 極性相反....  要High 才enable

By default, the limit pins are held normally-high with the Arduino's internal pull-up resistor. When a limit pin is low, Grbl interprets this as triggered. For the opposite behavior, just invert the limit pins by typing $5=1. Disable with $5=0. You may need a power cycle to load the change.
NOTE: If you invert your limit pins, you will need an external pull-down resistor wired in to all of the limit pins to prevent overloading the pins with current and frying them.

$22 - Homing cycle, bool

Ahh, homing. For those just initiated into CNC, the homing cycle is used to accurately and precisely locate a known and consistent position on a machine every time you start up your Grbl between sessions. In other words, you know exactly where you are at any given time, every time. Say you start machining something or are about to start the next step in a job and the power goes out, you re-start Grbl and Grbl has no idea where it is. You're left with the task of figuring out where you are. If you have homing, you always have the machine zero reference point to locate from, so all you have to do is run the homing cycle and resume where you left off.
To set up the homing cycle for Grbl, you need to have limit switches in a fixed position that won't get bumped or moved, or else your reference point gets messed up. Usually they are setup in the farthest point in +x, +y, +z of each axes. Wire your limit switches in with the limit pins and ground, just like with the hard limits, and enable homing. If you're curious, you can use your limit switches for both hard limits AND homing. They play nice with each other.
By default, Grbl's homing cycle moves the Z-axis positive first to clear the workspace and then moves both the X and Y-axes at the same time in the positive direction. To set up how your homing cycle behaves, there are more Grbl settings down the page describing what they do (and compile-time options as well.)
Also, one more thing to note, when homing is enabled. Grbl will lock out all G-code commands until you perform a homing cycle. Meaning no axes motions, unless the lock is disabled ($X) but more on that later. Most, if not all CNC controllers, do something similar, as it is mostly a safety feature to prevent users from making a positioning mistake, which is very easy to do and be saddenee when a mistake ruins a part. If you find this annoying or find any weird bugs, please let us know and we'll try to work on it so everyone is happy. :)
NOTE: Check out config.h for more homing options for advanced users. You can disable the homing lockout at startup, configure which axes move first during a homing cycle and in what order, and more.

2016年5月25日 星期三

grbl 05/25


1.   use  arduino  to update firmware...

https://github.com/Protoneer/GRBL-Arduino-Library

load grbl.zip to arduino library  and open the example and update it

2.    in grbl   library....   default baud rate is 9600  in  config.h

      change baud rate from 9600 to 115200....

3.
   default  define  DEFAULTS_GENERIC ...

   所以得到下面這些數值
    #define DEFAULT_X_STEPS_PER_MM 250.0
  #define DEFAULT_Y_STEPS_PER_MM 250.0
  #define DEFAULT_Z_STEPS_PER_MM 250.0

4.

Grbl 透過多種策略自動調整轉速來避免掉步發生
假設移動銑刀走一段直線, 那麼 Grbl 會以這樣的速度控制


X 軸是時間, Y 軸是速度, 中間的面積就是移動的距離
一開始逐漸加速, 抵達最大速度時維持定速, 快到目的地後開始減速
這樣的設計和馬達的力矩有關, 參考資料:Stepping Motion Profiles in Realtime
簡略敘述:
牛頓第二運動定律 F = m * a
馬達的力矩是有限的, 所以加速度也是受限的, 能產生的加速度和負載的質量有關
加上前篇 用 A4988 控制步進馬達 敘述, 馬達低速時力矩最大, 速度越快力矩最小
所以最高速度也是受限的, 如果快到連阻動力矩都克服不了時, 它就轉不動了
一旦操作馬達時超出這些限制, 它就會掉步, 然後又沒有感測器回報, 操作就會失敗
所以如果不依賴感測器, 就必須謹慎的管控這些速度和加速度, 這樣才可迴避掉步問題
步進馬達加速的形式還有別種選擇, 有興趣可 Google 搜尋 stepper speed profile

如果總是加上這些加速度限制, 理論上就可以正常的完成工作, 那是大多數的情況
源碼裡註解有提到, 當執行大量小幅度的線段時, 這種算法會惹上麻煩, 尤其曲線

上圖左下角有條曲線, 如果要讓機器畫出這種曲線, 通常它會被轉換成許多小線段
也就是紅色箭頭指處, 如果我們依照前面的加速度規則下去運算
每段線段的速度變化會變成藍色虛線指的地方, 也就是許多三角形
意思就是因為線段過短, 所以運轉時間也短, 它還來不及升到最高速就要開始減速了
這種曲線如果是從 G code 的曲線指令下的或許還有一些調整空間
但是有些模型轉換軟體會自動產生大量線段來趨近曲線
對 CNC 機器來說看到的就是一堆線段命令, 它沒法知道我們要做曲線
自然就是依照標準速度控制, 然後就會出現超龜速移動的現象
解決這問題 Grbl 用一種應該是數值方法的方式來計算
算法原文介紹:Improving Grbl: Cornering Algorithm
當兩條線段連在一起時, 規劃移動速度會如下圖

中間的 Vjunction 就是兩線段連結點的速度, 前一線段規劃速度時不會降到 0
而是降到一個計算過的值, 這個值和兩條線之間的夾角有關
如果這兩條線夾角接近 180 度, 那它就很可能是高密度曲線
就算不是, 這樣的夾角表示前進方向並沒有改變太多, 也不需要減速
如果這夾角是 90 度或更小, 表示它會進行大迴轉, 那就要確實減速
即使這線段是曲線的一部分也要這麼做, 算法如下:

Ventry 是第一條線的速度向量, Vexit 是第二條線的速度向量
這兩條線夾角為 θ, 綠色線 δ 為一常數, R 為圓的半徑
這個圓會接觸到兩向量且距離兩向量的接點距離為 δ
我們可以透過上圖右的算式求得 R 的長度
式子(1):基本 sin 函數, θ, δ, Ventry 邊組成的三角形來計算
式子(2)(3):把它左右搬移一下, 得到求 R 的算式
由於式子 (3) 可以用其下方的算式替換, 結果就是我們只要運用兩次開根號就求得解
雖然我們畫圖可以很清楚知道 θ 的角度, 但是 CNC 機器只收到該往哪裡移動
它並沒法知道夾角, 而求這夾角需要頭痛的 acos 來先找到 θ 值
對 AVR 這種 8-bit MCU 這會是運算負擔, Grbl 改用這來算可以大幅提昇效能
這段程式在 planner.c 中的 plan_buffer_line 函數

計算後求得 R, Vjunction = sqrt(a * R), a 是加速度值, 最大值由使用者設定
我很無聊的用 gnuplot 畫出 sqrt(R) 和線段夾角的曲線, 指令如

gnuplot -e "set terminal png size 800,400" \
-e "set xrange [0:210]" \
-e "set yrange [0:50]" \
-e "set xtics 15" \
-e "set xlabel \"Vector Angle (degrees)\"" \
-e "set ylabel \"sqrt(R)\"" \
-e "set key left" \
-e "plot \
  sqrt((0.02*sin((x*pi/180)/2))/(1-sin((x*pi/180)/2))) title 'Deviation = 0.02', \
  sqrt((0.1*sin((x*pi/180)/2))/(1-sin((x*pi/180)/2))) title 'Deviation = 0.1', \
  sqrt((1*sin((x*pi/180)/2))/(1-sin((x*pi/180)/2))) title 'Deviation = 1'" > gplot.png

結果:


或是直接上 Google!XD
搜尋:sqrt((0.02*sin(x/2))/(1-sin(x/2)))


Deviation 即是 δ 值, 它是一使用者設定的常數, Grbl 預設為 0.02
3D 印表機預設為 0.1, 1 是我無聊亂選的誇張值XD
可以發現夾角越接近 180 度 (Google 那張圖則是 3.14), Vjunction 就會越大
當曲線轉向幅度不大時它就不會減速, 就可以消除曲線龜速移動的問題
而當夾角縮小時, Vjunction 會掉的很快, 差不多到 135 度時就幾乎是減速到 0 再重新加速

計算完成後會放到 block_buffer 中
plan_buffer_line 會由 motion_control.c 裡的 mc_line 來呼叫
mc_line 會持續填充線段, 直到緩衝區滿了才開始做事情
然後呼叫 stepper.c 中的 st_prep_buffer 把計算過的速度資料存入步進馬達控制的緩衝區
接著就由 timer 中斷不斷的移動馬達, 採用 Bresenham 演算法, 中斷函數也在 stepper.c 中
算法可參考前篇:實作 Bresenham 直線演算法 
有點不同的是 Grbl 有 AMASS, 提高取樣頻率但走的步數相同, 目前不太懂其優點
Grbl 把這些線段資料以 block 命名, 而算完後進入步進馬達則是用 segment 命名
上面這些算完只有包含線段的起始速度和終止速度, 並不包含中間的速度控制
進入 st_prep_buffer 這函數後會根據起始速度和終止速度決定加速時間
這裡有一些乘除的算式, 變數名為 intersect_distance, 這裡我不太知道它怎算的
從變數名稱看來應該是求從起始速度加速以及從結束速度加速在中途的交點

Vs 為起始速度, Ve 為結束速度, X 軸為時間, Vi 為交叉點速度, 面積為距離
d1 是 Vs 加速到 Vi 走的距離, d2 是 Vi 減速到 Ve 走的距離, D 為總距離
根據 等加速度直線公式 假設加速度固定
應該可以寫出上圖右側的算式, 然後左右搬移後可以求得 d1 的距離
結果...我算出來的和程式裡的差一個運算符, 上圖右下有個紅色的減號, 程式裡是寫加號
我不太知道哪裡出錯了, 不過我相信應該是我錯的XD 這韌體已經佈署在很多機器上了
加上我數學不好, 不需要去質疑它的正確性XD
這裡算完後就可以決定這線段的速度變化曲線
有可能是梯形 (加速後恆定再減速, 即最上面第一張 v-t 圖)
也可能是三角形, 或是恆遞增, 恆遞減...等, 紀錄下後就可以讓步進馬達去運行

上面這些算式都是用來計算移動速度, 並不會動到移動的步數, 該走的步數當然是要確實走完
大致上就這樣, 這韌體還有許多細節, 像是兩端點偵測, 還有一些其他 G code 的功能
那些我目前先跳過, 目前走線方式我比較有興趣
了解移動細節後我們就可以設定原始碼以符合本機設定, 預設值會丟在 defaults.h
我只改這幾項

#ifdef DEFAULTS_GENERIC
  // Grbl generic default settings. Should work across different machines.
  #define DEFAULT_X_STEPS_PER_MM 1280.0
  #define DEFAULT_Y_STEPS_PER_MM 1280.0
  #define DEFAULT_Z_STEPS_PER_MM 1280.0
  #define DEFAULT_X_MAX_RATE 450.0 // mm/min
  #define DEFAULT_Y_MAX_RATE 450.0 // mm/min
  #define DEFAULT_Z_MAX_RATE 450.0 // mm/min
  #define DEFAULT_X_MAX_TRAVEL 200.0 // mm
  #define DEFAULT_Y_MAX_TRAVEL 270.0 // mm
  #define DEFAULT_Z_MAX_TRAVEL 170.0 // mm

DEFAULT_X_STEPS_PER_MM 1280.0
我的馬達加上 M8 螺桿, A4988 設 8 微步, 轉一圈 = 8 * 200 = 1600 微步
M8 的 pitch = 1.25mm, 所以每走 1mm 需要轉 1600 / 1.25 = 1280 微步

DEFAULT_X_MAX_RATE 450.0
這個要用量測的, 沒有馬達規格, 就手動下 G code, 用 feedrate 參數
下到空轉為止然後再減一點, 差不多就好XD

DEFAULT_X_MAX_TRAVEL 200.0
這是最大行程, 前篇有量測過, 直接輸入




 http://wukcsoft.blogspot.tw/2014/12/cnc-grbl.html


5.

一開始寫程式之前我是用 python3, 要裝幾個插件 (python3-serial) 才能跑, 程式在此:

#!/usr/bin/python3
import time
import serial # sudo apt-get install python3-serial
import sys

argc = len(sys.argv)

if argc < 3:
    print("Usage: " + sys.argv[0] + " <com_port> <gcode_file>")
    sys.exit()

print("Open COM port: " + sys.argv[1])
# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial(
    port = sys.argv[1],
    baudrate = 115200,
    parity = serial.PARITY_NONE,
    stopbits = serial.STOPBITS_ONE,
    bytesize = serial.EIGHTBITS,
    timeout = None,
    rtscts = None,
    dsrdtr = None
)
# wait for welcome message and flush it
# when open device node, cnc controller will reset and print message
time.sleep(1)
ser.flushInput()

# load ngc file
print("Open Gcode file: " + sys.argv[2])
f = open(sys.argv[2])
lines = f.readlines()
f.close()

# feed g codes line by line
for line in lines:
    if line[0] == ';':
        # drop comment
        continue;
    line_for_print = line.strip()
    print("Send: " + line_for_print)
    ser.write(str.encode(line))
    line_bytes = bytearray()
    while 1:
        c = ser.read()
        if c == b'\n':
            print("Recv: " + line_bytes.decode("utf-8"))
            break
        elif c >= b' ':
            # drop other control character such as 0xD
            line_bytes += c

ser.close()

程式就是開檔, 讀取 G code, 一行一行塞, 每塞一行就等, 等到回應再塞第二行
全部塞完就離開程式, 實驗過能用, 沒問題, 不過實際上使用會有問題
我們的土砲 CNC 並沒有自動回原位的功能, 一開機在哪裡, 哪裡就是原點
每次重新開啟 ttyACM 原點也會重設, 不知道是不是面對 USB 的 AVR 幹的好事
而我們的土砲機總是很隨意的選地方固定待加工品, 常常要先把銑刀移到參考點
然後才開始工作, 不預期的歸零我們會很困擾, 所以需要一個 UI
這 UI 總是維持 USB 連線, 手動移動完就讀 G code 發送, 不要中途歸零


桌上型電腦實現 UI 的方法很多, 也很雜, 所以我選了一個比較單純的環境

Android CNC !
UI API 可以跨版本直接執行, 也不用擔心用戶有沒有 OpenGL 驅動
因為你很難買到沒有 OpenGL 加速的 Android 平板 (其實硬要買還是有啦......(小聲))
不過裝置有限制, 因為要透過 USB, 平板上要有 USB Host 支援
目前看華碩 MeMO 系列應該都有, 我這台是 ME172V, 現在只要三張小朋友就有
加上無線網路, 自己架個網頁伺服器丟 G code 檔, 平板下載後開啟就能執行任務

原始碼附上:
AndroidCNC.zip

 http://wukcsoft.blogspot.tw/2014/12/cnc-if.html