首先向大家表示歉意,去年發帖展示初代平衡車時就已經承諾編輯出一份制作說明,但一直未能完成,很是抱歉!
原因有四:
1、 太過粗糙,發出來確實有礙觀瞻;
2、 上次做車沒留下幾張照片,無圖無真相;
3、 一直工作比較繁忙,實在沒能抽出時間;
4、 (接原因2)本意盡快再做一個,留下照片。但女皇遲遲不給批款,所以才延誤至今!
此次為了完成自己的承諾,可是耗費了2月的零花錢。并且有視頻有真相,希望您能喜歡。
原理簡介
“賽格威”平衡車
“賽格威”(英語:Segway)是一種電力驅動、具有自我平衡能力的個人用運輸載具,是都市用交通工具的一種。由美國發明家狄恩·卡門與他的DEKA研發公司(DEKA Research and Development Corp.)團隊發明設計,并創立思維車責任有限公司(Segway LLC.),自2001年12月起將思維車商業化量產銷售。(資料來源:維基百科中文)
“賽格威”是一種讓人留下深刻印象的代步工具,它占地不足一平方米,乘車人像使用滑板一樣站立其上,雙手解放,但卻可以僅通過身體移動改變重心位置,就進行前進后退,轉彎剎車等操作。傳統的交通工具都無法做到隨心而動,必須把大部分精力放在控制方向和速度上,而“賽格威”并不需要專門的操控裝置,一切由車身自主完成,也由此獲得了“平衡車”的別名。
“賽格威”平衡車看來神奇,但你有沒有發現它的原理其實很簡單呢?拜最新科技所賜,關鍵零件都可以在淘寶上直接買到,而控制程序也可以查閱原理自行編寫。擁有自己的平衡車,其實非常簡單。
倒立擺和機器人
“賽格威”的平衡問題,實際上是一個多級倒立擺問題。當一個人用手托住一根竹竿的底部使它在空中豎直不倒下,這就是一個一級倒立擺系統的模型。如果第一根竹竿上面用鉸鏈連著其他竹竿,或者竹竿本身具有一定的彈性(可比擬“賽格威”上的有骨骼和關節的大活人),就成了多級倒立擺。
用手撐竹竿的游戲很多人都玩過,印象最深的應當是它是一個靜不穩定系統。在桌面上的水杯能自己站穩,當重心投影落于杯底內時,即使有細小擾動也不會倒下。但是手心里的竹竿大部分時間重心投影不在接觸點上,讓竹竿保持相對不動靠的是動態調整——竹竿往哪邊倒,手就趕緊往哪邊湊,讓重心回到接觸點周圍。這就是依靠人眼,大腦和人手完成的動態平衡過程。
人類的大腦在處理這類問題上有先天優勢,因為人的走路過程本質上來說是不斷前跌的過程,必須依靠實時伸出支撐腳轉移重心來保證直立行進的動態平衡。而讓機器人做到這一點就很困難,需要綜合解決動態控制過程中的線性問題、魯棒性問題、鎮定問題、隨動問題以及跟蹤問題等諸多細節——所以至今見到的人形機器人里,能僵硬走路的很多,但能和真人一樣上躥下跳的絕無僅有。
兩名民警駕駛“賽格威”單人警用巡邏車巡邏。圖片來源:新華網
“賽格威”的動態平衡原理和倒立擺相同,將最上方的乘客作為擺臂,然后控制車輪維持系統重心使乘客直立。當駕駛人改變自己身體的角度往前或往后傾時,“賽格威”就會根據傾斜的方向前進或后退,而速度則與駕駛人身體傾斜的程度呈正比以保持平衡。這里的一個巧妙設計是將乘客傳感和控制二合一了——“賽格威”前進或后退維持平衡的同時,也達成了按乘客意圖前進或后退的目的。最終,熟練的駕駛人可以和自己行走一樣,僅憑直覺就能完成前后左右各方向的運動,同時解放雙手和大腦思維,這一特點使“賽格威”特別適合游覽和警用巡邏。
DIY自己的“賽格威”
和人類行走一樣,“賽格威”的控制也需要傳感器和致動器。它依靠MEMS技術制造的精密固態陀螺儀和加速度計感應車體的旋轉,速度和傾斜,高速微處理器計算傳感器數據,并驅動輪轂電機完成前進/后退/差速轉彎的動作。而在電路之外,為了讓它從實驗室中的倒立擺變成實用的代步車,還需要準備一些必需的結構零件和附件。
機械部分
此次設計的機械機構包括一個簡單的獨立懸掛。緩沖部分直接采用自行車的避震器(需要更換彈簧),機體做得不很緊湊,主要為了能夠拆卸折疊,便于收放和運輸。(需要說明的是,結構已提交專利申請,請勿用于商業用途。)
整機材料很簡單,兩個獨立驅動的輪子+電機驅動板+車身角度傳感器+轉彎傳感器+電池+一個裝下這些東西的盒子 。兩個輪子、電機、避震器都是來自淘寶的成品。鈑金和機加件為單獨加工。
這里貼一些制作圖片,詳細的零件工程圖列在最后。
整機外形
結構細節
電機安裝部分
電機為優耐特電機,250W,24v/質量不好,不作推薦。
電機法蘭部分剖視
轉向機部分:
整機背面
裝配過程
鋰電池倉
原設計為鉛酸電池,后一朋友為我無償提供了鋰電池,在此再次表示感謝。
車銑加工
電機法蘭安裝
整體安裝
電路部分
主控采用AVR的ATMEGA_32,電機驅動為H橋驅動方式,元件選用的IR2184和IRF1405。傳感器選用IDG300和ADXL335,電流傳感器為ACS755。另外還有一些外圍的小功能,可有可無,不詳述了。
控制驅動PCB圖
傳感器PCB圖
PCB空板
焊接需要注意的就是——別太馬虎就行。先焊低矮的元器件,再焊大個的!
焊接基本完成
連接電機測試
散熱器:
遙控和語音模塊
控制程序部分
這里就提一些關鍵部分,一些個人認為有用的代碼附在最后。
流程圖
車身角度獲取
選用的傳感器為模擬量輸出,因此只需要用單片機的AD采集數據后計算出角度值即可,需要注意的是,采集后的數據直接使用效果會很糟糕。需要再次進行濾波計算,得到一個準確、及時、抗擾動的真實角度數據。調速過程中可以用串口將數據輸出,輔助調試。
計算車輪速度
這里就是簡單的PID控制車輪轉速,如果不記得就百度看看。調試參數會花點時間,剛開始參數別調過大,否則抖動起來有危險!另外需要設置角度過大停機的功能。
獲取轉向數據
轉向數據為采集轉向電位器而來,采集后的數據進行濾波處理后再用。轉向中間設置一個無效的死區,也是防止誤動作。
遙控
遙控為最普通的4鍵遙控器,淘寶成品。
語音
語音選用成品語音模塊,廠家提供完整說明文檔。
溫度
硬件原先選用18b20,很是遺憾這部分程序沒調通,可能原因1:系統必須有多處中斷,并且中斷服務程序比較多,因而打亂了18b20的時序,加上沒有示波器,因而沒調通。可能原因2:智商問題。
嘗試調試了近2小時無果后改用模擬量溫度芯片LM35D,電壓直接由電阻分壓而來。
其余部分可自由發揮。
視頻演示
無視頻無真相,怕熊上門所以拍了一小段視頻。
客廳實在太小,還放了些雜物,能夠行走的地方就只有中間一小塊了,跑不開。
友情提示:此車有一定危險性,不排除摔倒、失控等問題,在空地上玩玩就好,打算用來代步上班的,請給自己買好保險!
附件1:零件工程圖
點擊下載完整工程圖(文件大小:6.15M)(本設計已提交專利申請,請勿用于商業用途。)
附件2:重點代碼
2.1車身角度濾波代碼
/************濾波************/
float P[2][2] = {{ 1, 0 },{ 0, 1 }};
float Pdot[4] ={0,0,0,0};
const char C_0 = 1;
float q_bias, angle_err, PCt_0, PCt_1, E, K_0, K_1, t_0, t_1;
float Q_angle=0.001, Q_gyro=0.003, R_angle=0.5, dt=0.01;
void Kalman_Filter(float angle_m,float gyro_m)
{
angle+=(gyro_m-q_bias) * dt;
Pdot[0]=Q_angle - P[0][1] - P[1][0];
Pdot[1]=- P[1][1];
Pdot[2]=- P[1][1];
Pdot[3]=Q_gyro;
P[0][0] += Pdot[0] * dt;
P[0][1] += Pdot[1] * dt;
P[1][0] += Pdot[2] * dt;
P[1][1] += Pdot[3] * dt;
angle_err = angle_m - angle;
PCt_0 = C_0 * P[0][0];
PCt_1 = C_0 * P[1][0];
E = R_angle + C_0 * PCt_0;
K_0 = PCt_0 / E;
K_1 = PCt_1 / E;
t_0 = PCt_0;
t_1 = C_0 * P[0][1];
P[0][0] -= K_0 * t_0;
P[0][1] -= K_0 * t_1;
P[1][0] -= K_1 * t_0;
P[1][1] -= K_1 * t_1;
angle += K_0 * angle_err;
q_bias += K_1 * angle_err;
angle_dot = gyro_m-q_bias;
}
//**************濾波*****************//
static float C_angle,C_angle_dot;
static float bias_cf;
void Complement_filter(float angle_m_cf,float gyro_m_cf)
{
bias_cf=0.998*bias_cf+0.002*gyro_m_cf;
C_angle_dot=gyro_m_cf-bias_cf;
C_angle=0.98*(C_angle+C_angle_dot*0.02)+0.02*angle_m_cf;
}
//***************************** 濾波結束*********************************/
2.2 轉向數據處理代碼
/************轉向************/
void Steering_handle(void)
{
Buf= 0.9 *Buf + 0.1 * AD_Turn;
Turning= Buf -Turn_Zero; //
if(Turning 《- Turn_Dead) //死區
Turning+=Turn_Dead;
else if(Turning》 Turn_Dead)
Turning-=Turn_Dead;
else Turning= 0;
if (mode==0)
{
Drive_A=0;
Drive_B=0;
if (!(angle》0.1||angle《-0.1))
{
mode=1;
}
}
else
{
if(lab==0)
{
Turning=0;
}
else if (Turning》55||Turning《-55)//
{
Turning=0;
lab=3;// turn error
}
else //按車速整定轉向數據
{
//buf2=Drivespeed;
//if (buf2《0)buf2*=-1;
//buf2/=3;
//Turning/=buf2;
Turning/=1;
}
Drive_A=Drivespeed-Turning;
Drive_B=Drivespeed+Turning;
}
}
//***************************** 轉向結束*********************************/
2.3遙控部分狀態機
/***********按鍵********/
#define BOOL int
#define FALSE 0
#define TRUE 1
#define INT8U unsigned int
/**********硬件接口***********/
#define KEYPIN1 (PINC&(1《《3))
#define KEYPIN2 (~PINB&(1《《0))
#define KEYPIN3 (~PINB&(1《《1))
#define KEYPIN4 (~PINB&(1《《3))
#define KEYPIN5 (~PINB&(1《《4))
/**********按恪鍵屬性**********/
#define KEY_JT 0x0e
#define KEY_A 0x0d
#define KEY_B 0x0b
#define KEY_C 0x07
#define KEY_D 0x08
#define KEY_NULL 0x0f
//
#define KEY_LONG_PERIOD 250
#define KEY_CONTINUE_PERIOD 25
//
#define KEY_DOWN 0x80
#define KEY_LONG 0x40
#define KEY_CONTINUE 0x20
#define KEY_UP 0x10
//
#define KEY_STATE_INIT 0
#define KEY_STATE_WOBBLE 1
#define KEY_STATE_PRESS 2
#define KEY_STATE_LONG 3
#define KEY_STATE_CONTINUE 4
#define KEY_STATE_RELEASE 5
uchar KeyScan(void)
{
if(KEYPIN2==0) return KEY_A;
if(KEYPIN3==0) return KEY_B;
if(KEYPIN4==0) return KEY_C;
if(KEYPIN5==0) return KEY_D;
if(KEYPIN1==0) return KEY_JT;
return KEY_NULL;
}
void GetKey(uchar *pKeyValue)
{
static char KeyState = KEY_STATE_INIT;
static char KeyTimeCount = 0;
static char LastKey = KEY_NULL;
char KeyTemp = KEY_NULL;
KeyTemp = KeyScan();
switch(KeyState)
{
case KEY_STATE_INIT:
{
if(KEY_NULL!=(KeyTemp))
{
KeyState = KEY_STATE_WOBBLE;
}
}
break;
case KEY_STATE_WOBBLE:
{
KeyState = KEY_STATE_PRESS;
}
break;
case KEY_STATE_PRESS:
{
if(KEY_NULL!=(KeyTemp))
{
LastKey = KeyTemp;
KeyTemp|=KEY_DOWN;
KeyState = KEY_STATE_LONG ;
}
else
{
KeyState = KEY_STATE_INIT;
}
}
break;
case KEY_STATE_LONG:
{
if(KEY_NULL !=(KeyTemp))
{
if(++KeyTimeCount 》 KEY_LONG_PERIOD)
{
KeyTimeCount = 0;
KeyTemp|=KEY_LONG;
KeyState = KEY_STATE_CONTINUE;
}
}
else
{
KeyState = KEY_STATE_RELEASE;
}
}
break;
case KEY_STATE_CONTINUE:
{
if(KEY_NULL !=(KeyTemp))
{
if(++KeyTimeCount 》 KEY_CONTINUE_PERIOD)
{
KeyTimeCount = 0;
KeyTemp |= KEY_CONTINUE;
}
}
else
{
KeyState = KEY_STATE_RELEASE;
}
}
break;
case KEY_STATE_RELEASE:
{
LastKey |=KEY_UP;
KeyTemp = LastKey;
KeyState = KEY_STATE_INIT;
}
break;
default:break;
}
*pKeyValue = KeyTemp;
}
2.4電池電壓
void Get_Batt_Volt(void)
{
int buf3=0,b=0;
buf3=0.9*buf3+0.1*AD_Batt;
if (b》10)
{
Voltage=buf3*3000.0/1024/65;
b=10;
}
else
{
b++;
}
}
-
DIY
+關注
關注
176文章
886瀏覽量
348281 -
平衡車
+關注
關注
7文章
80瀏覽量
26952
發布評論請先 登錄
相關推薦
評論