电子DIY奇才自造超炫赛格威平衡车

首先向大家表示歉意,去年发帖展示初代平衡车时就已经承诺编辑出一份制作说明,但一直未能完成,很是抱歉!
原因有四:
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++;
}
}

小进智能音箱亮相2018用户见面会
光度立体 | 机器视觉中的PS
HKC T2751U显示器:定义设计师理想新标配
浅析光伏逆变器的工作原理
扫地机器人的工作原理/硬件构成/发展前景
电子DIY奇才自造超炫赛格威平衡车
新能源汽车颠覆传统动力总成之后,“技术本田”还能保持一如既往的领先吗?
人工智能给我们带来了怎样的改变
苹果代工厂仁宝遭遇勒索软件攻击
脉冲电源工作原理是什么
USB接口的电气特性
泛林回应美国AI芯片出口管制新规:预计不会产生实质性影响
Arduino锁的制作教程
200MHz主频的C28x 核以及 CLA 的协处理器
CKS32F4xx系列产品Timer的基本使用方法-比较输出
节卡机器人亮相车展,摇身变成顶级咖啡师,助力某知名车企服务升级
LG 正式推出HU810P 4K 激光投影仪:支持苹果的 AirPlay 2和 WebOS 5.0 系统
下一代AI将是什么样子?
python常用的内置函数和模块
变压器220v转12v怎么接线