[开源]阿莫蜘蛛进度报告[2015-02-15更新]
本帖最后由 Gorgon_Meducer 于 2015-2-15 22:31 编辑此贴专门用于阿莫蜘蛛项目进度报告
傻孩子工作室作品
STAFF
Hardware Design yiming988
Driver Design y574924080
Movement Design qufuta sunfulong
Mechanical Designing 老陈
Production 老陈
Architecture Design Gorgon_Meducer
SPONSOR
amobbs, Freescale
ACKNOWLEDGE
Armok
- 2015-2-15
a. 更新Joint类,增加Time属性可以控制动作在指定的时间内精确的完成,大幅度提升蜘蛛舞蹈的时间精度。
工程模板需要在IAR7.x下编译,单击这里下载:
特别说明,工程文件在“SpiderRobot_Template\application\template\build_iar”下面
- 2015-2-04
a. 更新第一版舞蹈
http://v.youku.com/v_show/id_XODg2MDIwOTA0.html
讨论在这里
http://www.amobbs.com/forum.php?mod=viewthread&tid=5614299&page=1#pid8405770
感谢sunfulong的精彩作品!
- 2015-1-28
a. 发布工程模板v1.4a
- 提供宏模板,用以将指定的动作重复指定的次数,范例:
ACTION_REPEATE( REF_ACTION(Spider_Go_Front), //!< target action
10, //!< times
REF_STATE(Spider_Action1));
重复执行动作(Spider_Go_Front)10次,完成后直接切换到Spider_Action1状态
- 提供宏模板,用以延时指定的时间(非阻塞延时),范例:
ACTION_DELAY_MS(4000, //!< delay time in ms
REF_STATE(Spider_Action2)); //!< next state
延时4000ms,完成后直接切换到Spider_Action2状态
- 提供脚本模式,并提供范例脚本,演示了重复前进10步,延时4秒,后退10步,前进一步的简单脚本
/*! \brief play action script
*! \param none
*! \return access result
*/
bool play_action_script(void)
{
utilitis_init();
return NEW_FSM(SpiderPlay, REF_STATE(Spider_Init));
}
IMPLEMENT_FSM(SpiderPlay)
PRIVATE STATE(Spider_Init) BEGIN
IO_CFG(
{PB20, IO_WORKS_AS_GPIO, IO_PULL_UP},
);
GSP_PORTB.DIR &= ~PB20_MSK;
TRANSFER_TO_STATE(Spider_Wait_for_Trigger);
EXIT_STATE;
END
PRIVATE STATE(Spider_Wait_for_Trigger) BEGIN
//! press key to start
if (!(GSP_PORTB.IN & PB20_MSK)) {
TRANSFER_TO_STATE(Spider_Play);
}
REFLEXIVE_STATE
END
PRIVATE STATE(Spider_Play) BEGIN
ACTION_REPEATE( REF_ACTION(Spider_Go_Front), //!< target action
10, //!< times
REF_STATE(Spider_Action1));
EXIT_STATE;
END
PRIVATE STATE(Spider_Action1) BEGIN
ACTION_DELAY_MS(4000, //!< delay time in ms
REF_STATE(Spider_Action2)); //!< next state
EXIT_STATE
END
PRIVATE STATE(Spider_Action2) BEGIN
ACTION_REPEATE(REF_ACTION(Spider_Go_Back), 10, REF_STATE(Spider_Action3));
EXIT_STATE
END
PRIVATE STATE(Spider_Action3) BEGIN
CALL_ACTION_EX( REF_ACTION(Spider_Go_Front), //!< target action
REF_STATE(Spider_Action4)); //!< next
EXIT_STATE
END
PRIVATE STATE(Spider_Action4) BEGIN
//! performance complete!!! wait for trigger again
TRANSFER_TO_STATE(Spider_Wait_for_Trigger);
EXIT_STATE
END
END_IMPLEMENT_FSM
b. 整理工程架构,提供动作模板和脚本模板
这里actions文件夹保存了所有的单元动作脚本,你可以通过action_template.c作为模板建立新的单元动作。
步骤如下:
1. 复制action_template.c,并重新命名为action_xxxxxx.c,这里xxxxx是新单元动作的名字
2. 利用action_xxxxxxx.c里面的模板完成动作的编写
3. 再actions.h里面加入新动作的声明,例如:
EXTERN_ACTION(Spider_Go_Front);
EXTERN_ACTION(Spider_Go_Back);
EXTERN_ACTION(Spider_XXXXX);
script.c就是我们编写舞蹈脚本的地方,里面已经放入了Demo的最简单脚本。所有在actions.h里面加入的
动作在这里都可以直接使用。你可以方便的对某些动作进行重复,加入延时,等等。
c. 修正了随机偶发的动作无法正确执行的错误
d. 提供控制台模式
- 用户可以通过控制台绕过上层系统直接对目标舵机进行控制
-用户可以通过控制台对单个joint进行校准
工程模板需要在IAR7.x下编译,单击这里下载:
特别说明,工程文件在“SpiderRobot_Template\application\template\build_iar”下面
- 2015-1-4
a. 完成硬件的基本调试。完成基本充放电循环测试。完成基本舵机负载测试。由于买骨架的时候忘记拍螺丝了,
目前只能干瞪眼,哈哈哈……淘宝卖家说单独买螺丝要15RMB一包,且只能直接用支付宝转账……心头一丝
疑虑,然后我有问,可否我拍下一个别的东西,然后你改下价格?对方说,我拍什么,仓库出什么……
所以决定等老陈的蜘蛛骨架。
感谢yiming988牺牲业余时间完成了新版蜘蛛的驱动电路。感谢老陈对硬件设计做出的指导。
上图:
- 2014-12-29
a. 拿到了完全重新设计的驱动电路PCB。新的PCB上加入了两并18650电池的可编程充电和保护电路,并
直接使用mini USB进行充电,去掉了普通的DC电源接口,这样设计的目的是可以充分利用大家积攒在抽屉
里的各种手机平板充电器,包括各类充电宝。我们在PCB背面加入了一个2并 电池盒的封装,从而将重心
集中在蜘蛛的中轴上,从而平衡各个关节的受力,并尽可能降低重心,同时解决蜘蛛的供电问题。
新的电路设计由老陈监督,即便如此,错误和 疏漏也可能在所难免,如果大家发现了问题,请及时帮助我们,
我们有耐心,会坚持一版一版的加以改进。
- 2014-11-24
a. 收到飞思卡尔官方寄回的改进版蜘蛛 V1 Plus 开源项目重启。
通过这个改进版的蜘蛛,我们发现同样是10RMB的舵机……这品质差别咋就这么大捏~
飞思卡尔委托第三方“剽窃者剽窃者剽窃者剽窃者剽窃者剽窃者剽窃者剽窃者剽窃者蓝宙”进行了骨架的细小改进,并修正了我们硬件上的错误。非常感谢。另外,这
舵机……这尼玛舵机……真的是10RMB……真爽啊……哈哈哈哈,推荐莫老大搞一批咱们可以做活动了。
- 2014-9-3
a. 更新cerebel
1. 将cerebel抽象出来,剥离了与18舵机系统的强耦合,使其成为32以内舵机系统的通用控制服务模块
对Joint的初始化和校准部分被从cerebel服务中剥离开来,放在templete.c中。
2. 在cerebel内部加入了动作缓冲队列,用户通过CEREBEL.Move() 方法传入的Actions可以是栈分配的
局部变量,也就是说使用ACTION宏的时候,填充的内容不必是编译时刻就确定了的常量,可以是变
量,或者其它运行时刻经过计算获得的值。通过缓冲队列,用户可以源源不断的加入事先编排好的动
作序列而不必等待先前加入的动作完成。
b. 更新Joint
增加了一个接口IsComplete
//! \name joint interface
//! @{
DEF_INTERFACE(i_joint_t)
bool (*Init) (joint_t *ptJoint, const joint_cfg_t *ptCFG);
void (*Finish) (joint_t *ptJoint);
bool (*Start) (joint_t *ptJoint);
bool (*Stop) (joint_t *ptJoint);
const joint_cfg_t *(*Info)(joint_t *ptJoint);
bool (*TurnTo) (joint_t *ptJoint, int32_t nAngle, int32_t nSpeed);
bool (*TurnToInTime) (joint_t *ptJoint, int32_t nAngle, uint32_t wTime);
struct {
bool (*Set) (joint_t *ptJoint, int32_t nAngle);
int32_t (*Get) (joint_t *ptJoint);
} Angle;
struct {
bool (*Set) (joint_t *ptJoint, int32_t nSpeed);
int32_t (*Get) (joint_t *ptJoint);
} AngularSpeed;
void (*TimerServiceRoutine)(void);
struct {
bool (*Register) (joint_t *ptJoint, DELEGATE_HANDLE *ptHandler);
bool (*Unregister) (joint_t *ptJoint, DELEGATE_HANDLE *ptHandler);
bool (*IsComplete) (joint_t *ptJoint);
}ActionCompleteEvent;
END_DEF_INTERFACE(i_joint_t)
//! @}
工程模板需要在IAR7.x下编译,单击这里下载:
特别说明,工程文件在“SpiderRobot_Template\application\template\build_iar”下面
- 2014-8-31
a. 完成了时间插补算法,更新了Joint算法
b. 增加了一个运动控制服务cerebel
通过cerebel我们可以抽象出Action的概念。Action是多个舵机的在某个时刻的运动状态片段,就好比你通过
摄像机拍摄高速运动的蜘蛛可以获得一系列的照片一样,在这个照片上,每个关节(Joint)都有对应的位置,
而Action就是这样的照片。cerebel的思路是,我们只要给它提供一系列的照片,并告诉他播放照片的速度,
cerebel就会自动的按照指定的时间播放动作,例如,下面就是一连串代表前进的动作:
ACTION_CFG(
ACTION(
ALL_LEGS_MSK ,
true, //!< start automatically
1 << JOINT_TIME_UNIT_RESOLUTION, //!< Use 1s
{
= 135,
= 180,
= 225,
= 45,
= 0,
= 315,
= 200,
= 200,
= 200,
= 335,
= 335,
= 335,
= 115,
= 120,
= 115,
= 65,
= 65,
= 65,
}
),
ACTION(
TRI_LEGS_L1_MSK | TRI_LEGS_L0_MSK | TRI_LEGS_R1_MSK | TRI_LEGS_R2_MSK,
true, //!< start automatically
1 << JOINT_TIME_UNIT_RESOLUTION, //!< Use 1s
{
= 225,
= 225,
= 315,
= 135,
= 180,
= 23,
= 0,
= 0,
= 180,
= 90,
= 90,
= 90,
}
),
ACTION(
TRI_LEGS_L1_MSK | TRI_LEGS_L2_MSK,
true, //!< start automatically
1 << JOINT_TIME_UNIT_RESOLUTION, //!< Use 1s
{
= 180,
= 180,
= 0,
= 90,
= 90,
= 90,
}
),
ACTION(
TRI_LEGS_L0_MSK | TRI_LEGS_R0_MSK,
true, //!< start automatically
1 << JOINT_TIME_UNIT_RESOLUTION, //!< Use 1s
{
= 180 - 23,
= 225,
= 338,
= 0,
= 270,
= 225,
}
),
ACTION(
TRI_LEGS_R0_MSK | TRI_LEGS_R1_MSK | TRI_LEGS_R2_MSK,
true, //!< start automatically
1 << JOINT_TIME_UNIT_RESOLUTION, //!< Use 1s
{
= 45,
= 350,
= 180 - 23,
= 315,
= 315,
= 200,
= 45,
= 45,
= 135,
}
),
ACTION(
TRI_LEGS_R1_MSK | TRI_LEGS_R2_MSK,
true, //!< start automatically
1 << JOINT_TIME_UNIT_RESOLUTION, //!< Use 1s
{
= 0,
= 0,
= 180,
= 90,
= 90,
= 90,
}
),
ACTION(
TRI_LEGS_R0_MSK | TRI_LEGS_L0_MSK | TRI_LEGS_L1_MSK | TRI_LEGS_L2_MSK,
true, //!< start automatically
1 << JOINT_TIME_UNIT_RESOLUTION, //!< Use 1s
{
= 135,
= 180,
= 23,
= 225,
= 225,
= 315,
= 135,
= 135,
= 45,
= 0,
= 315,
= 180 + 22,
}
),
);
这里ACTION_CFG和ACTION宏是对cerebel的封装,详细封装方法请参考cerebel.h。通过cerebel蜘蛛的运动
就可以通过“照片”的方式描述下来,而照片与照片之间的部分则由cerebel和joint的时间插补算法自动处理,
从而产生平滑的运动(运动的快慢由照片与照片之间的时间间隔来控制)。
这里,我详细介绍下ACTION_CFG和ACTION的使用方法。
首先,我们必须对整个蜘蛛建立一个统一的数学模型,不然我们就没有一个统一的标准来描述舵机的位置:
根据这个位置,我们首先要完成对舵机的角度映射,这里我们要借助Joint类,Joint初始化中要根据图中标识
的范围来设置角度的Offset,舵机的实际运动范围等等,下面,我们以右后腿为例,介绍Joint的初始化:
JOINT_CFG(
this.ptJoints,
RIGHT_BACK_LEG_0,
225, //!< offset
0, //!< start angle
180, //!< end angle
180, //!< Max Angular Speed
315, //!< Reset Position
1024, //!< K
);
结合上面的图,我们知道右后腿(RIGHT_BACK_LEG_0,这里的0,1,2表示每个腿由身体一次向外的3个关节),
的运动范围是225°~45°的180度范围。所以,我们将offset配置为225。舵机的实际运动范围就是0°~180°。
这里,我们还配置了最大的角速度为180°/s,关节复位时候关节的角度315°。关于起始角和终止角,这个实际
上是用来限定舵机的安全运动范围的,比如虽然舵机可以0°~180°之间运动,但有时候,机械上并不允许这么
大的运动范围,所以必须要限定一个起始角和终止角。以右后腿为例,为了防止右后腿和右中退产生碰撞,我
们限定右后腿的实际运动范围是45°~135°(90°范围),修改后的初始化如下:
JOINT_CFG(
this.ptJoints,
RIGHT_BACK_LEG_0,
225, //!< offset
45, //!< start angle
135, //!< end angle
180, //!< Max Angular Speed
315, //!< Reset Position
1024, //!< K
);
不幸的是10块钱的舵机线性度太差,所以,我们需要根据安装的实际情况对舵机的角度进行两点矫正,矫正
方法如下:
1、首先将起始角和终止角分别设定为0°和180°,并通过复位位置将舵机运动到第一个点,比如270°的点
JOINT_CFG(
this.ptJoints,
RIGHT_BACK_LEG_0,
225, //!< offset
0, //!< start angle
180, //!< end angle
180, //!< Max Angular Speed
270, //!< Reset Position
1024, //!< K
);
修正offset使得关节物理上到达270°。我的舵机修正的结果如下:
JOINT_CFG(
this.ptJoints,
RIGHT_BACK_LEG_0,
222, //!< offset
0, //!< start angle
180, //!< end angle
180, //!< Max Angular Speed
270, //!< Reset Position
1024, //!< K
);
每个舵机都是不同的,你需要自己校准。完成上述步骤后,计算出实际的起始角,并更新起始角和终止角的信
息:start_angle = first_angle_point - offset。例如我们第一点是270°,offset是222度,那么起始角就是48°
(48 = 270°- 222),更新配置如下:
JOINT_CFG(
this.ptJoints,
RIGHT_BACK_LEG_0,
222, //!< offset
48, //!< start angle
180, //!< end angle
180, //!< Max Angular Speed
270, //!< Reset Position
1024, //!< K
);
修改复位值,使关节运动到下一个校准点,比如0°,这个时候,这里的K就发挥作用了,修改比例K来调整实际
的物理位置,使关节运动到0°。至此,我们就完成了一个关节的校准,我的校准结果如下:
JOINT_CFG(
this.ptJoints,
RIGHT_BACK_LEG_0,
222, //!< offset
48, //!< start angle
138, //!< end angle
180, //!< Max Angular Speed
315, //!< Reset Position
900, //!< K
);
也许你已经意识到了,这个校准真TMD的坑啊……18个舵机都要这么校准……而且离开量角器……用肉眼看角度有
多苦逼你可以想象的吧?而且10块钱的舵机还有温票啊……温票啊……漂啊……啊……如果你和我一样处女座,喜
欢精确——恭喜你……对了对了——忘记给你说了,机械上有个概念叫做回程差……也就是说,你从不同的方向运
动到同一个角度实际上停下来的位置是不同的……这种情况下,你只能祈祷,舵机不要坏,我不用再换一个……
到了这里,大约用了4个小时,你就可以完成全部18个舵机的校准。这下我们可以很开心的通过JOINT的接口来
指挥关节运动到指定的角度了,这里的角度参考图上的坐标系,不用担心实际舵机自己的坐标系,接口如下:
//! \name joint interface
//! @{
DEF_INTERFACE(i_joint_t)
bool (*Init) (joint_t *ptJoint, const joint_cfg_t *ptCFG);
void (*Finish) (joint_t *ptJoint);
bool (*Start) (joint_t *ptJoint);
bool (*Stop) (joint_t *ptJoint);
const joint_cfg_t *(*Info)(joint_t *ptJoint);
bool (*TurnTo) (joint_t *ptJoint, int32_t nAngle, int32_t nSpeed);
bool (*TurnToInTime) (joint_t *ptJoint, int32_t nAngle, uint32_t wTime);
struct {
bool (*Set) (joint_t *ptJoint, int32_t nAngle);
int32_t (*Get) (joint_t *ptJoint);
} Angle;
struct {
bool (*Set) (joint_t *ptJoint, int32_t nSpeed);
int32_t (*Get) (joint_t *ptJoint);
} AngularSpeed;
void (*TimerServiceRoutine)(void);
struct {
bool (*Register) (joint_t *ptJoint, DELEGATE_HANDLE *ptHandler);
bool (*Unregister) (joint_t *ptJoint, DELEGATE_HANDLE *ptHandler);
}ActionCompleteEvent;
END_DEF_INTERFACE(i_joint_t)
//! @}
extern const i_joint_t JOINT;
这里,我们可以直接用JOINT.TurnTo() 方法来将关节以指定的速度运动到指定的绝对角度;也可以使用
JOINT.TurnToInTime()方法将关节在指定的时间内匀速的运动到指定的角度。这里的时间(wTime)单位
是1/8秒。写8或者(1<<JOINT_TIME_UNIT_RESOLUTION) 就是1秒。另外,你也可以通过JOINT.Angle.Set()
来指定关节的角度,通过JOINT.Angle.Get()来实时获取当前的角度。这里需要说明下,以上介绍的方法,
并不会立即生效,一定要调用JOINT.Start() 让新的设置生效,当然你也可以随时调用JOINT.Stop()来个
急刹车。
最后,补充说明下JOINT.TurnTo()是速度插补算法,JOINT.TurnToInTime() 是时间插补算法,认真说来
时间插补算法更有用,但实际上时间插补算法是建立在速度插补算法之上的,大家可以参考我的源代码。
这里的速度插补算法产生的是匀速运动,如果你想产生一个S型加减速,可以自己实现一个,我个人觉得
这对时间插补算法应该是没有影响的——如果你封装的好的话。
好,言归正传,我们来说说cerebel咋用。首先你要定义18个Joint,然后通过CEREBEL_CFG对他们初始化,
实际上,我们辛辛苦苦校准好的舵机信息就在cerebel服务的初始化函数里面:
NO_INIT static joint_t s_tJoints;
...
static void spider_init(void)
{
//! initialize servo service
SERVO.Init ();
SERVO.Start ();
//! initialize cerebel layer
CEREBEL_CFG(
s_tJoints,
UBOUND(s_tJoints),
);
...
}
接下来,我们就可以用ACTION_CFG和ACTION来设计舵机动作了,这里就着这两个宏的定义,介绍下各个参数
的意义:
#define ACTION(__MSK, __AUTO, __TIME, ...) \
{ \
(__MSK), \
(__AUTO), \
(__TIME), \
.nAngles = __VA_ARGS__, \
}
#define ACTION_CFG(...) \
do { \
static cerebel_action_t tActions[] = {__VA_ARGS__}; \
CEREBEL.Action.Move(tActions, UBOUND(tActions)); \
} while(false)
从代码里很容易知道ACTION_CFG并没有什么神秘的,只是用了C99对可变参数宏的一个技巧,也就是"..."对应
__VA_ARGS__。这个宏定义了一个静态的照片(Actions)数组,并调用了CEREBEL.Action.Move方法。
ACTION()有三个参数,分别是:
__MSK:用以指定后面的角度配置列表中哪些角度配置是有效的,具体参考template\interface.h里面的joint_msk_t
__AUTO: 用以指定当前Action是否立即生效(true表示立即生效,false则要调用CEREBEL.Start()才会生效)
__TIME: 时间插补算法参数,单位是1/8秒。简单说就是多长时间内匀速完成这个Action。
... : 这个就是具体的角度配置明细表了,我们看一个例子:
ACTION_CFG(
ACTION(
TRI_LEGS_L1_MSK | TRI_LEGS_L2_MSK,
true, //!< start automatically
1 << JOINT_TIME_UNIT_RESOLUTION, //!< Use 1s
{
= 180,
= 180,
= 0,
= 90,
= 90,
= 90,
}
),
);
这个ACTION涉及到三条腿(左前,左后,右中三条腿组成的三角形,TRI_LEG_Ln, 这里n取0,1,2),动作立即
生效,1秒钟内匀速完成。后面花括号里面是具体每个关节的角度信息,需要注意的是,这里的关节是与前面的MSK
相对应的,例如TRI_LEGS_L1_MSK 和TRI_LEGS_L2_MSK的实际内容是:
typedef enum {
...
TRI_LEGS_L1_MSK = ( LEFT_FRONT_LEG_1_MSK |
LEFT_BACK_LEG_1_MSK |
RIGHT_MIDDLE_LEG_1_MSK),
TRI_LEGS_L2_MSK = ( LEFT_FRONT_LEG_2_MSK |
LEFT_BACK_LEG_2_MSK |
RIGHT_MIDDLE_LEG_2_MSK),
...
) joint_msk_t;
OK,到了这里,小伙伴们,平台已经搭建完成,请愉(tong)快(ku)的(de)玩(bei)耍(nue)吧。硬件
坏了,舵机烧了,我去愉快的画圈圈去了……我靠……^%^*+#€£><#{&$@
工程模板需要在IAR7.x下编译,单击这里下载:
特别说明,工程文件在“SpiderRobot_Template\application\template\build_iar”下面
- 2014-8-30
a. 经过一天的冲刺,终于完成了蜘蛛每一个舵机的校准——真心累啊
心得:10块钱的舵机,一致性真心差,还有温漂……你让他运动90度(不是运动到90度),不同的舵机会走出
完全不同的角度来……不得不用了线性补偿算法,这是今天一天的成果……累死人啊……如果大家以后玩这个套
件,每一套都要自己辛苦校准的。
do {
NO_INIT static joint_t s_tJoint;
NO_INIT static DELEGATE_HANDLE s_tHandler;
// SERVO.Angle(RIGHT_BACK_LEG_2, 90);
JOINT_CFG(
s_tJoint,
LEFT_BACK_LEG_2,
355, //!< offset
50, //!< start angle
140, //!< end angle
180, //!< Max Angular Speed
90, //!< Reset Position
680, //!< K
);
JOINT_CFG(
s_tJoint,
LEFT_MIDDLE_LEG_2,
353, //!< offset
52, //!< start angle
142, //!< end angle
180, //!< Max Angular Speed
90, //!< Reset Position
660, //!< K
);
JOINT_CFG(
s_tJoint,
LEFT_FRONT_LEG_2,
353, //!< offset
52, //!< start angle
142, //!< end angle
180, //!< Max Angular Speed
90, //!< Reset Position
680, //!< K
);
JOINT_CFG(
s_tJoint,
RIGHT_BACK_LEG_2,
355, //!< offset
50, //!< start angle
140, //!< end angle
180, //!< Max Angular Speed
90, //!< Reset Position
780, //!< K
);
JOINT_CFG(
s_tJoint,
RIGHT_MIDDLE_LEG_2,
0, //!< offset
45, //!< start angle
135, //!< end angle
180, //!< Max Angular Speed
90, //!< Reset Position
860, //!< K
);
JOINT_CFG(
s_tJoint,
RIGHT_FRONT_LEG_2,
354, //!< offset
51, //!< start angle
141, //!< end angle
180, //!< Max Angular Speed
90, //!< Reset Position
840, //!< K
);
JOINT_CFG(
s_tJoint,
RIGHT_BACK_LEG_1,
275, //!< offset
40, //!< start angle
180, //!< end angle
180, //!< Max Angular Speed
0, //!< Reset Position
1100, //!< K
);
JOINT_CFG(
s_tJoint,
RIGHT_MIDDLE_LEG_1,
275, //!< offset
40, //!< start angle
180, //!< end angle
180, //!< Max Angular Speed
0, //!< Reset Position
1024, //!< K
);
JOINT_CFG(
s_tJoint,
RIGHT_FRONT_LEG_1,
260, //!< offset
55, //!< start angle
180, //!< end angle
180, //!< Max Angular Speed
0, //!< Reset Position
970, //!< K
);
JOINT_CFG(
s_tJoint,
RIGHT_BACK_LEG_0,
222, //!< offset
48, //!< start angle
138, //!< end angle
180, //!< Max Angular Speed
315, //!< Reset Position
900, //!< K
);
JOINT_CFG(
s_tJoint,
RIGHT_MIDDLE_LEG_0,
260, //!< offset
55, //!< start angle
145, //!< end angle
180, //!< Max Angular Speed
0, //!< Reset Position
850, //!< K
);
JOINT_CFG(
s_tJoint,
RIGHT_FRONT_LEG_0,
313, //!< offset
47, //!< start angle
137, //!< end angle
180, //!< Max Angular Speed
45, //!< Reset Position
920, //!< K
);
JOINT_CFG(
s_tJoint,
LEFT_FRONT_LEG_0,
40, //!< offset
50, //!< start angle
140, //!< end angle
180, //!< Max Angular Speed
135, //!< Reset Position
890, //!< K
);
JOINT_CFG(
s_tJoint,
LEFT_MIDDLE_LEG_0,
80, //!< offset
55, //!< start angle
145, //!< end angle
180, //!< Max Angular Speed
180, //!< Reset Position
920, //!< K
);
JOINT_CFG(
s_tJoint,
LEFT_BACK_LEG_0,
142, //!< offset
38, //!< start angle
128, //!< end angle
180, //!< Max Angular Speed
225, //!< Reset Position
1130, //!< K
);
JOINT_CFG(
s_tJoint,
LEFT_BACK_LEG_1,
105, //!< offset
20, //!< start angle
110, //!< end angle
180, //!< Max Angular Speed
180, //!< Reset Position
1100, //!< K
);
JOINT_CFG(
s_tJoint,
LEFT_MIDDLE_LEG_1,
90, //!< offset
45, //!< start angle
135, //!< end angle
180, //!< Max Angular Speed
180, //!< Reset Position
950, //!< K
);
JOINT_CFG(
s_tJoint,
LEFT_FRONT_LEG_1,
80, //!< offset
55, //!< start angle
145, //!< end angle
180, //!< Max Angular Speed
180, //!< Reset Position
850, //!< K
);
} while(false);
b. 终于能站起来了……发图庆贺
明日计划:
a. 完成运动的时间插补算法——简单说就是给定每个关节目标角度以及多长时间内到达目标角度,算法会自动
调整角速度使得关节在指定的时间点匀速运动到指定的角度。
b. 借助时间插补算法,我需要采样至少6组舵舵机的运动角度信息(每组18个舵机的角度信息),然后以播放
的形式实现前进的动作。对于后退的动作也需要6组这样的采样点。同理,左转,右转,向左横着走,向右
横着走。愿主保佑我能顺利完成。
- 2014-8-27
a. 更新工程模板
> 修正了舵机控制的BUG
> 增加了一个舵机运动插补和极坐标系映射的服务Joint
Joint服务是一个运动插补的服务,它使得我们可以让指定的舵机以指定的角速度(度/秒)
平滑的运动到指定的绝对角度。每个舵机的运动范围是0~180度,但是随着安装位置的不同
舵机实际上缺乏一个相对同一个坐标系的绝对角度的定位能力。所以,一旦完成了舵机的
安装,我们就可以借助Joint服务来给舵机指定绝对的角度——当然,舵机实际可以运动的
角度范围之差不会超过180度。
static void servo_demo(void)
{
SERVO.Init ();
SERVO.Start ();
do {
NO_INIT static joint_t s_tJoint;
NO_INIT static DELEGATE_HANDLE s_tHandler;
JOINT_CFG(
s_tJoint,
LEFT_BACK_LEG_0,
45, //!< offset
0, //!< start angle
180, //!< end angle
180, //!< Max Angular Speed
135,
);
} while(false);
}
这是一个Joint服务的例子,从初始化可以看出,目标舵机是左后腿的第一个关节(LEFT_BACK_LEG_0),
相对蜘蛛的绝对坐标系,这个关节的实际运动角度范围是(45+0°)~(45+180°),最大允许的角速度
是180°/s,复位状态下关节的绝对角度是135°。
这段代码随后在完成初始化后,又命令关节s_tJoint以45°/s的速度平滑的运动到绝对角度45°。
JOINT.TurnTo(&s_tJoint, 45, 45);
Joint服务还提供运动完成事件,用户可以注册事件处理程序(一个事件可以同时注册多个事件处理程序):
//! \brief joint action complete event handler
static fsm_rt_t joint_action_complete_event_handler(void *pArg, void *pParam)
{
joint_t *ptJoint = (joint_t *)pArg;
static bool s_bFlag = false;
if (s_bFlag) {
JOINT.TurnTo(ptJoint, 45, 45);
} else {
JOINT.TurnTo(ptJoint, 135, 45);
}
s_bFlag = !s_bFlag;
return fsm_rt_cpl;
}
static void servo_demo(void)
{
...
do {
...
NO_INIT static DELEGATE_HANDLE s_tHandler;
...
delegate_handler_init(&s_tHandler,
&joint_action_complete_event_handler,
&s_tJoint );
JOINT.ActionCompleteEvent.Register(&s_tJoint, &s_tHandler);
} while(false);
...
}
这里函数joint_action_complete_event_handler()是s_tJoint的运动完成事件处理函数,我们在这个
事件处理程序里面用了类似乒乓操作的方式,让关节在45°和135°之间来回运动。注册事件需要借助
DELEGATE_HANDLE类型的对象的帮助,我们需要先初始化它,告诉他事件处理函数是什么,要附带
的对象是什么(这里我们把当前的Joint对象作为参数传递给了事件处理程序)。通过register方法,
我们可以很方便的注册事件处理程序。
Joint服务需要两个接口的支持,一个是1024分之一秒的定时中断服务,一个是用来设置指定舵机的角度
的函数。前者我们通过SysTick的中断处理程序来实现,后者我们封装了一个接口适配器:
ISR(SysTick_Handler)
{
static uint32_t s_wCounter;
s_wCounter++;
if (!(s_wCounter & (_BV(10) - 1))) {
/* run code every 1s here, you can add debug code here*/
}
JOINT.TimerServiceRoutine();
}
/*! \note initialize application
*\param none
*\retval true hal initialization succeeded.
*\retval false hal initialization failed
*/
ROOT bool app_init(void)
{
servo_demo();
/*! you can put your code here */
SYSTICK_CFG (
ENABLE_SYSTICK |
SYSTICK_SOURCE_SYSCLK |
ENABLE_SYSTICK_INTERRUPT,
(PM_GET_SYS_CLK() >> 10)
);
ENABLE_GLOBAL_INTERRUPT();
//! start a new task
return NEW_STATIC_FSM(DemoTask, REF_STATE(Demo_Init));
}
//! \brief interface adapter for joint service
void joint_set_servo(joint_no_t tID, uint32_t wAngle)
{
SERVO.Angle(tID, wAngle);
}
通过这些接口Joint就能很好的为我们服务了。 下一步我们就可以利用Joint服务为每一个舵机建立一个Joint对象
从而提供统一的运动插补和坐标系映射服务。这为我们后面的矢量控制算法,以及PC/Andriod脚本控制平台提供
了准备。
附上Joint服务的接口定义:
//! \name joint interface
//! @{
DEF_INTERFACE(i_joint_t)
bool (*Init) (joint_t *ptJoint, const joint_cfg_t *ptCFG);
void (*Finish) (joint_t *ptJoint);
bool (*TurnTo)(joint_t *ptJoint, int32_t nAngle, int32_t nSpeed);
struct {
bool (*Set) (joint_t *ptJoint, int32_t nAngle);
int32_t (*Get) (joint_t *ptJoint);
} Angle;
struct {
bool (*Set) (joint_t *ptJoint, int32_t nSpeed);
int32_t (*Get) (joint_t *ptJoint);
} AngularSpeed;
void (*TimerServiceRoutine)(void);
struct {
bool (*Register) (joint_t *ptJoint, DELEGATE_HANDLE *ptHandler);
bool (*Unregister)(joint_t *ptJoint, DELEGATE_HANDLE *ptHandler);
}ActionCompleteEvent;
END_DEF_INTERFACE(i_joint_t)
//! @}
工程模板需要在IAR7.x下编译,单击这里下载:
特别说明,工程文件在“SpiderRobot_Template\application\template\build_iar”下面
- 2014-8-20
a. 更新了工程模板
> 使用了板载的50MHz外部时钟源
> 在工程模板中增加了SysTick产生1s中断的例子
ISR(SysTick_Handler)
{
static uint32_t s_wCounter;
s_wCounter++;
if (!(s_wCounter & (_BV(10) - 1))) {
/* run code every 1s here, you can add debug code here*/
}
}
/*! \note initialize application
*\param none
*\retval true hal initialization succeeded.
*\retval false hal initialization failed
*/
bool app_init(void)
{
/*! you can put your code here */
SYSTICK_CFG (
ENABLE_SYSTICK |
SYSTICK_SOURCE_SYSCLK |
ENABLE_SYSTICK_INTERRUPT,
(PM_GET_SYS_CLK() >> 10)
);
ENABLE_GLOBAL_INTERRUPT();
servo_demo();
//! start a new task
return NEW_STATIC_FSM(DemoTask, REF_STATE(Demo_Init));
}
> 在工程模板中增加了三色LED的例子,用以演示状态机调度器的使用
/*============================ MACROS ========================================*/
#define TOP (0x01FF)
/*============================ MACROFIED FUNCTIONS ===========================*/
#define LED_RED_ON() do { GSP_PORTB.OUTCLR |= PB22_MSK; } while(false)
#define LED_RED_OFF() do { GSP_PORTB.OUTSET |= PB22_MSK; } while(false)
#define LED_GREEN_ON() do { GSP_PORTE.OUTCLR |= PE26_MSK; } while(false)
#define LED_GREEN_OFF() do { GSP_PORTE.OUTSET |= PE26_MSK; } while(false)
#define LED_BLUE_ON() do { GSP_PORTB.OUTCLR |= PB21_MSK; } while(false)
#define LED_BLUE_OFF() do { GSP_PORTB.OUTSET |= PB21_MSK; } while(false)
#define RGB(__R, __G, __B) \
do { \
set_led_red_gradation(__R); \
set_led_green_gradation(__G); \
set_led_blue_gradation(__B); \
} while(false)
#define LED_LIGHT 3
#define LED_BREATH_SPEED 14
/*! \brief set the 16-level led gradation
*! \param hwLevel gradation
*! \return none
*/
static void set_led_red_gradation(uint16_t hwLevel)
{
static uint16_t s_hwCounter = 0;
if (hwLevel >= s_hwCounter) {
LED_RED_ON();
} else {
LED_RED_OFF();
}
s_hwCounter++;
s_hwCounter &= TOP;
}
/*! \brief set the 16-level led gradation
*! \param hwLevel gradation
*! \return none
*/
static void set_led_green_gradation(uint16_t hwLevel)
{
static uint16_t s_hwCounter = 0;
if (hwLevel >= s_hwCounter) {
LED_GREEN_ON();
} else {
LED_GREEN_OFF();
}
s_hwCounter++;
s_hwCounter &= TOP;
}
/*! \brief set the 16-level led gradation
*! \param hwLevel gradation
*! \return none
*/
static void set_led_blue_gradation(uint16_t hwLevel)
{
static uint16_t s_hwCounter = 0;
if (hwLevel >= s_hwCounter) {
LED_BLUE_ON();
} else {
LED_BLUE_OFF();
}
s_hwCounter++;
s_hwCounter &= TOP;
}
static void breath_led(void)
{
static uint16_t s_hwCounter = 0;
static int16_t s_nGrayRed = 0;
static int16_t s_nGrayGreen = 0;
static int16_t s_nGrayBlue = 0;
s_hwCounter++;
if (!(s_hwCounter & (_BV(LED_BREATH_SPEED)-1))) {
s_nGrayRed++;
if (s_nGrayRed == (TOP >> LED_LIGHT)) {
s_nGrayRed = 0;
}
}
if (!(s_hwCounter & (_BV(LED_BREATH_SPEED+1)-1))) {
s_nGrayGreen++;
if (s_nGrayGreen == (TOP >> LED_LIGHT)) {
s_nGrayGreen = 0;
}
}
if (!(s_hwCounter & (_BV(LED_BREATH_SPEED + 2)-1))) {
s_nGrayBlue++;
if (s_nGrayBlue == (TOP >> LED_LIGHT)) {
s_nGrayBlue = 0;
}
}
RGB(
ABS(s_nGrayRed - (TOP >> (LED_LIGHT + 1))),
ABS(s_nGrayGreen - (TOP >> (LED_LIGHT + 1))),
ABS(s_nGrayBlue - (TOP >> (LED_LIGHT + 1)))
);
}
/*! state machine implementation */
IMPLEMENT_FSM(DemoTask)
//! state: Demo_Init
PRIVATE STATE(Demo_Init) BEGIN
/*! put your application service code here */
GSP_PORTB.DIR |= PB22_MSK | PB21_MSK;
GSP_PORTE.DIR |= PE26_MSK;
GSP_PORTB.OUTSET = PB22_MSK | PB21_MSK;
GSP_PORTE.OUTSET = PE26_MSK;
IO_CFG(
{PB22,IO_WORKS_AS_FUNC1}, //!< LED Red
{PE26,IO_WORKS_AS_FUNC1}, //!< LED Green
{PB21,IO_WORKS_AS_FUNC1}, //!< LED Blue
);
TRANSFER_TO_STATE(Demo_Task);
REFLEXIVE_STATE;
END
//! state: Demo Task
PRIVATE STATE(Demo_Task) BEGIN
/*! put your none-blocked task code here */
breath_led();
REFLEXIVE_STATE;
END
END_IMPLEMENT_FSM
工程模板需要在IAR7.x下编译,单击这里下载:
特别说明,工程文件在“SpiderRobot_Template\application\template\build_iar”下面
- 2014-8-16
a. 完成了硬件调试,只有一个地方需要飞下线,还算不错,谢谢大家为我们的团队成员出谋划策,我们从中学习了很多
b. 完成了工程模板的第一个公测版Beta1
我们对舵机进行了封装,提供如下的接口:
//! servo interface
//! \name i_servo_t
//! @{
DEF_INTERFACE (i_servo_t)
bool (*Init) (void);
bool (*Deinit) (void);
bool (*Finish) (void);
bool (*Start) (void);
bool (*Angle) (uint8_t chServoNum,uint32_t wAngle);
END_DEF_INTERFACE (i_servo_t)
//! @}
/*============================ PROTOTYPES ====================================*/
/*============================ GLOBAL VARIABLES ==============================*/
extern const i_servo_t SERVO;
使用起来也很简单,我们在template.c里面提供了代码范例:
static void servo_demo(void)
{
SERVO.Init ();
for (uint8_t n = 0; n <= 18; n++) {
SERVO.Angle (n, 90);
}
SERVO.Start ();
// SERVO.Angle (0, 90);
// SERVO.Angle (1, 0);
// SERVO.Angle (2, 45);
// SERVO.Angle (3, 10);
// SERVO.Angle (4, 20);
// SERVO.Angle (5, 10);
// SERVO.Angle (6, 15);
// SERVO.Angle (7, 29);
}
我们这次以SDK的形式提供对这个平台的支持,用户将看不到main.c,也看不到main函数,对用户来说,应用所需
所有入口函数我们都提供了,例如硬件初始化,这个函数是main函数实际调用的第一个函数,其返回值决定了SDK默认
的一些底层初始化是否要执行,true表示要执行,false表示不再执行。目前默认的底层硬件初始化只是处理了一些时钟
上的配置,建议这里不要返回false。这个例子里面,我们展示了如何初始化IO,将其配置为对应的PWM功能。关于IO
的配置,请参考io.h。
/*! \brief hardware initialization
*! \param none
*! \retval true run the default initialization
*! \retval false ignore the default initialization
*/
ROOT bool ON_HW_INIT(void)
{
/*! you can put your code here */
IO_CFG(
{PC1, IO_WORKS_AS_FUNC4},
{PC2, IO_WORKS_AS_FUNC4},
{PC3, IO_WORKS_AS_FUNC4},
{PC4, IO_WORKS_AS_FUNC4},
{PC8, IO_WORKS_AS_FUNC3},
{PC9, IO_WORKS_AS_FUNC3},
{PC10,IO_WORKS_AS_FUNC3},
{PC11,IO_WORKS_AS_FUNC3},
{PD0, IO_WORKS_AS_FUNC4},
{PD1, IO_WORKS_AS_FUNC4},
{PD2, IO_WORKS_AS_FUNC4},
{PD3, IO_WORKS_AS_FUNC4},
{PD4, IO_WORKS_AS_FUNC4},
{PD5, IO_WORKS_AS_FUNC4},
{PB18,IO_WORKS_AS_FUNC3},
{PB19,IO_WORKS_AS_FUNC3},
{PA1, IO_WORKS_AS_FUNC3},
{PA2, IO_WORKS_AS_FUNC3},
);
return true;
}
用户另外一个要用到的就是应用初始化app_init。这个app_init是main函数完成所有既定的初始化工作以后,调用的
最后一个初始化函数——也就是用户自己的初始化函数。用户应该在这里做他自己想做的事情,但最后一定要起一个
状态机任务,就像例子里面提供的那样,不然,离开超级循环,大家怎么活?
//! \brief demo task
STATIC_FSM(DemoTask)
PRIVATE STATE(Demo_Init);
PRIVATE STATE(Demo_Task);
END_STATIC_FSM
...
/*! \note initialize application
*\param none
*\retval true hal initialization succeeded.
*\retval false hal initialization failed
*/
bool app_init(void)
{
servo_demo();
//! start a new task
return NEW_STATIC_FSM(DemoTask, REF_STATE(Demo_Init));
}
/*! state machine implementation */
IMPLEMENT_FSM(DemoTask)
//! state: Demo_Init
PRIVATE STATE(Demo_Init) BEGIN
/*! put your application service code here */
TRANSFER_TO_STATE(Demo_Task);
REFLEXIVE_STATE;
END
//! state: Demo Task
PRIVATE STATE(Demo_Task) BEGIN
/*! put your none-blocked task code here */
REFLEXIVE_STATE;
END
END_IMPLEMENT_FSM
工程模板需要在IAR7.x下编译,单击这里下载:
特别说明,工程文件在“SpiderRobot_Template\application\template\build_iar”下面
c. 由于我最近身体不舒服,在家休养,所以动作调试上耽搁了很多,团队的小伙伴很努力,本周稍后应该会有一个简单的
演示。
下一步计划:
a. 发布工程模板Beta2,提供与此次开源直接相关的蜘蛛运动控制相关的模块及其源代码,修正Beta1发现的问题
b. 完成基本的动作调试,考虑要不要重新做一版硬件来解决飞线的问题。
- 2014-8-9
a. 拿到了PCB,组装了下,还蛮好看的,硬件还在调试
b. 完成了K6x的工程模板,需要IAR7.x的支持,争取下周放出公测版
c. 初步完成单爪动作和封装
下一步计划:
a. 尝试用基本的单爪动作组合出所需的运动,比如,横着爬,前进后退等
b. 完成硬件调试
c. 开放工程模板公测
- 2014-8-3
a. PCB 已经送出去,粘贴原理图如下
特别说明,由于本团队硬件能力非常有限,如果发现任何不合理的地方请及时指出,当前硬件设计未经过严格验证
不推荐作为参考设计,仅供本项目第一阶段验证和测试使用。
b. Software Framework 基本完成
目前暂时基于K20开发了HAL层。已经调试完成。
c. 蜘蛛的运动控制部分正在测试,近几天可以完成基本的单爪运动状态机封装。
下一步计划:
a. 完成蜘蛛运动抽象层的底层部分,即爪子的行为模式封装(比如爪子的弯曲,爪子的收缩),为高层运动模式做准备
(比如,横着走,比如前后走)
b. 完成K6x的Software Framework扩展
顶顶 老师!{:smile:} 该顶 该顶 我感觉你的电源口画的有问题 怒赞一个! 学习学习。
我的就用洞洞板搭了。 走线有点杂,先调功能吧,估计后续要改版。{:loveliness:} 等着看效果了,哈哈 好期待 顶傻孩子老师~~~ 进度好快啊,嫉妒羡慕中,PCB布局很好 上面的固定孔是用螺丝固定吗?实际安装时是否会碰到周围器件? 顶,关注中~~{:lol:} ” 蜘蛛的运动控制”,专业。 赞一个 速度好快 都构架好Framework了 支持 赞赞赞,这就是效率 {:victory:}{:victory:}{:victory:} 器件放置挺整齐 1个光耦1rmb。而且这个光耦不是用于隔离作用,只是做个电平转换,这成本太高了
LM2576的不轮layout的体积,还是成本感觉都不是最好的。这个电源部分可以大部分的压缩空间和成本 myiccdream 发表于 2014-8-4 10:32
1个光耦1rmb。而且这个光耦不是用于隔离作用,只是做个电平转换,这成本太高了
LM2576的不轮layout的体积, ...
硬件是我做的,软件工程师做硬件真是有点勉为其难了,您能把更好的方案说的详细一点吗? 光耦在这里确实是隔离用,控制和舵机的地是分开的,只是光耦这么用不知合不合适,更经济的光耦型号有哪些;供电部分2576确实占面积而且贵,我也是手头有这个所以直接用了,您有改进的选型能说一下吗?蜘蛛测试版做完后我可以在第二版上改进 我从原理图上看你所使用的是非隔离的电源(隔离电源可以参考金升阳的模块)。所以我个人觉得这个地方不算严格的隔离。当然您这样使用光耦也没什么问题
。
假定您的电源输入为12V。那么我用过的电源芯片如下
比如TI的5A的TP5450, 3A的TPS5430
MPS的3A MP2303 MP1495 .2A的MP1470
或者RT8105
不在乎速度光耦换成TLP281-4GB;4光耦,SOP16封装。
开关电源用TPS54331 ;3.5V 至 28V 输入、3A、570kHz 降压转换器
myiccdream 发表于 2014-8-4 11:36
我从原理图上看你所使用的是非隔离的电源(隔离电源可以参考金升阳的模块)。所以我个人觉得这个地方不算严 ...
嗯 确实不算隔离,用隔离的dcdc模块确实是更好的选择,多谢指点! hhxb 发表于 2014-8-4 11:39
不在乎速度光耦换成TLP281-4GB;4光耦,SOP16封装。
开关电源用TPS54331 ;3.5V 至 28V 输入、3A、570kHz...
查了一下这个光耦的资料,确实是更好的选择, 电源IC也比2576更适合,谢谢! hhxb 发表于 2014-8-4 11:39
不在乎速度光耦换成TLP281-4GB;4光耦,SOP16封装。
开关电源用TPS54331 ;3.5V 至 28V 输入、3A、570kHz...
下一版可能选择金升阳的模块,不过这一版先只能这样将就了{:smile:} 电源没有隔离 光耦没有起到隔离作用VIN多少伏?7805有2.5V压降这个功耗不容忽视。 顶,学习学习 如果只是为了完成将3.3V电压到5V 电压转换(非隔离),普通三极管就可胜任,若后面的负载有点重,需要驱动DC 马达之类的负载,则可考用MOS管来兼做电平转换和驱动,供参考。 期待这种有质量的代码。喜欢这种分层。 各个接口之间的距离挨的有点近,将来对线插拔的时候可能下不了手。特别是阵列布的3pin的那些接口 电源插座的孔是否有问题? 非常感谢大家!! 顶,学习学习 学习,顶顶 学习,支持傻孩子
傻孩子大大厉害=-= 大力关注傻孩子{:lol:}{:lol:} yiming988 发表于 2014-8-4 11:09
硬件是我做的,软件工程师做硬件真是有点勉为其难了,您能把更好的方案说的详细一点吗? 光耦在这里确实 ...
GND,PGND,WGND没有隔离开吧,这样即使用光耦也是起不到隔离作用的
光耦可以用TLP185GB,比较便宜 (TLP181似乎停产了) 顶一下、、、 速度好快哈。。。 PCB上,DC电源座的封装没问题吗? 支持,期待大师的软件。 原来傻孩子团队用AD9画板子啊 画的不错啊 这块板子是傻孩子画的? 赞LZ的 快速度 效率真高,赞 期待早日成功 关注中,电源器件后面的建议不错 sbk100 发表于 2014-8-6 21:59
原来傻孩子团队用AD9画板子啊 画的不错啊 这块板子是傻孩子画的?
我不懂画板子。 很多东西都不会,只能默默支持了。。。 支持,期待最后结果!~~~ sbk100 发表于 2014-8-6 21:59
原来傻孩子团队用AD9画板子啊 画的不错啊 这块板子是傻孩子画的?
板子是傻孩子老师委托我画的,我本意是将舵机电源与核心板电源隔离,仓促画板竟然把PGND和GND连起来了,不过供测试用还是没有问题的。
突然感觉,我越来越像一个软件工程师了(硬件已经渐行渐远{:dizzy:}默哀,撒花。。。) 进度更新了 好厉害好礼哈啊 傻孩子好厉害,赶紧做出来看看啥样 进度还是挺快的 动作好快,顶个 大神啊 佩服 好东西,期待成品 想加入 不知如何才能 期待后续进展{:smile:} 看了大家的的评论学到了不少
楼主速度很快啊 {:victory:}{:victory:}顶一下、、、 不错啊,以后可以改进成家庭服务机器人,满屋子的爬呀爬。 哇,期待下一步动作 更新了工程模板的Beta1。 期待,大工程啊 顶顶,珍惜辛勤劳动成果 老师注意身体呀。。 不错不错 工程模板棒极了,又跟着学着不少东西 工程模板不错,学习了
同时楼主多注意身体呀! 再次顶一个 这个不错哈。。。。 不错,继续保持关注中··· 更新了,增加了GPIO操作的例子,使用外部50MHz精确时钟,增加了SysTick的使用范例 赶上更新了,看看 更新蛮快的嘛{:lol:}期待。。。。。。。。。。。。 来学习了 Gorgon_Meducer 发表于 2014-8-9 23:33
进度更新了
老师能讲一下,三色灯驱动程序吗?
还有宏定义为什么要do while 结构,不是直接一个语句呢?{:2_36:} 三色LED驱动不错,学习了 进度很快啊,期待大神的程序,从傻孩子的文章里学到很多。 傻孩子大牛一个啊,学习 Pupil 发表于 2014-8-20 20:08
老师能讲一下,三色灯驱动程序吗?
还有宏定义为什么要do while 结构,不是直接一个语句呢?...
可以避免被宏进行文字替换的时候由于上下文的原因产生歧义。 更新真快啊,赞一个,不久就能看到作品了 不错。。。。。。。。。。。。。 学习楼主的资料 封装的太严实反而感觉不好用了! jlhgold 发表于 2014-8-22 12:58
封装的太严实反而感觉不好用了!
蜘蛛平台专注的是运动控制,至于怎么发生PWM,这是大家都知道的东西,没必要增加编译的难度。
专注于应用实现而不是驱动开发,这才是做项目应有的态度。 支持一下 Gorgon_Meducer 发表于 2014-8-22 13:21
蜘蛛平台专注的是运动控制,至于怎么发生PWM,这是大家都知道的东西,没必要增加编译的难度。
专注于应用 ...
那为啥要把main函数封装掉?如果PWM是中断自动产生的 何必在意买了怎么写 只要告知必须要假如某个初始化函数即可 感觉有一个团队能够合作的很好,那么效率会非常高的 大牛,每个地方都有可以学习的内容! jlhgold 发表于 2014-8-22 19:15
那为啥要把main函数封装掉?如果PWM是中断自动产生的 何必在意买了怎么写 只要告知必须要假如某个初始化 ...
PWM硬件产生,而且和上层无关,没有使用中断。PWM的初始化函数就是SERVO.Init()
不提供 main是一种格式上的规范。用户并没有损失任何灵活性和可能性。由于main里面
有一些固定的结构,为了避免被破坏导致系统服务失效,所以采取了这种结构。其实如果
你非常介意这个问题,同时又不想用调度器,直接把app_init当作main函数就可以了,你
可以在里面加上超级循环。 我是来向高手学习的 顶顶 进来学习一下软件思路。
把灵活的变机械,把机械的变灵活。 程序写的太牛了,羡慕中
状态机编程,架构搭的不错!