No.1
前言
最近有幸参加了一期RT-Thread官方发起的RT-Robot Car DIY活动,跟着大神们的步伐我也成功的做出了一辆麦克纳姆轮PS2遥控车,心里非常的Happy,特意记录了这个制作过程用作给小白们借鉴。不多逼逼了,来开始我们造车之旅。
关于选材
初次探索智能车本着节约成本和最低风险的原则,我们尽量选用现成的硬件材料。在探索成功后,学会了理解了其中的原理,再根据自己的需求完全设计自己的小车。(以下所有图片,点击即可查看大图)
小车制作仓:https://github.com/bluesky-ryan/snowstorm_car
官方Robot-car连接:https://github.com/RT-Thread-packages/rt-robot
1、主控芯片:我们选用淘宝成品主控板(主控芯片STM32F103RCT6
2、底盘:麦克纳姆轮底座,某宝多的是自行选购
3、电机:买底座基本都带电机,我们选用带AB编码器的1:30减速电机
4、遥控:普通SONY PS2遥控,30-40块钱
5、电池:选用3S 11V航模电池
6、线:USB转串口线一根
No.2
核心知识点
1.RT-Thread bsp移植。
2.STM32-CubeMXs使用。
3.RTOS使用。
4.PID控制理论。
5.麦克拉姆拉控制理论。
6.简单运动模型
开发环境
使用Keil V5作为编译器
使用 RT-Thread 最新版本
使用自己移植的 bsp
No.3
详细步骤
BSP移植
关于BSP的移植RT-Thread官网有非常详细的文档描述:
移植过程不做累述,按照官方的步骤一步一步的走即可。
先只配置控制台串口和系统呼吸灯:
console串口:UART2
系统LED灯:PD2
电机控制
主控板电机驱动芯片为TI的DIV8833芯片,一颗DIV8833芯片可以驱动两个电机,我们有4个电机用到了2个芯片。芯片采用对偶PWM方波输入驱动,频率手册没写,我们先使用10KHZ。
STM32F103RTC6高级定时器1、8都带有对偶PWM输出,我们的主控板用的高级定时器1,悲剧的是定时器1只能输出3路对偶PWM方波和1路普通PWM方波,可是我们有4个轮子,所以最后一个轮子的对偶极只能GPIO来代替控制了。
驱动逻辑表:
驱动资料有了逻辑也清晰了,我们接下要做的就只是按照要求输出几个PWM方波了
第一步:CubMX配置定时器1为PWM对偶模式
第二步:封装初始化、通道控制等电机控制接口(具体封装参照源码motor.c文件),最后给上层提供一个初始化接口,一个通道速度控制接口。(向?滑动查看全部)
1/**
2*@ingroup motor
3*
4*初始化定时器
5*@param none
6*@retrun none
7*/
8staticvoidmoto_pwm_init(void)
9{
10
11/* USER CODE BEGIN TIM1_Init 0 */
12
13/* USER CODE END TIM1_Init 0 */
14
15TIM_ClockConfigTypeDef sClockSourceConfig = { 0};
16TIM_MasterConfigTypeDef sMasterConfig = { 0};
17TIM_OC_InitTypeDef sConfigOC = { 0};
18TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = { 0};
19
20/* USER CODE BEGIN TIM1_Init 1 */
21
22/* USER CODE END TIM1_Init 1 */
23htim1.Instance = TIM1;
24htim1.Init.Prescaler = 71;
25htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
26htim1.Init.Period = MOTOR_PWM_MAX - 1;
27htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
28htim1.Init.RepetitionCounter = 0;
29htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
30if(HAL_TIM_Base_Init(&htim1) != HAL_OK)
31{
32Error_Handler;
33}
34sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
35if(HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
36{
37Error_Handler;
38}
39if(HAL_TIM_PWM_Init(&htim1) != HAL_OK)
40{
41Error_Handler;
42}
43sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
44sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
45if(HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
46{
47Error_Handler;
48}
49sConfigOC.OCMode = TIM_OCMODE_PWM1;
50sConfigOC.Pulse = 0;
51sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
52sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
53sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
54sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
55sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
56if(HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
57{
58Error_Handler;
59}
60if(HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
61{
62Error_Handler;
63}
64if(HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
65{
66Error_Handler;
67}
68if(HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
69{
70Error_Handler;
71}
72sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
73sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
74sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
75sBreakDeadTimeConfig.DeadTime = 0;
76sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
77sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_LOW;
78sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
79if(HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
80{
81Error_Handler;
82}
83/* USER CODE BEGIN TIM1_Init 2 */
84
85/* USER CODE END TIM1_Init 2 */
86HAL_TIM_MspPostInit(&htim1);
87
88HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
89HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
90HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
91HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
92
93LOG_I( “motor pwm initialization ok.
”);
94
95}
1/**
2*@ingroup motor
3*
4*控制电动机标量控制,正直表示正转,负值表示反转
5*
6*@param ch 控制通道MOTOR_CH1/TMOTOR_CH2/TMOTOR_CH3/MOTOR_CH4,可组合使用MOTOR_CH_1|MOTOR_CH_2
7*@param speed pwm控制量[-1000, 1000]
8*@retrun none
9*/
10voidmotor_pwm_set(motor_chx ch, int16_tspeed)
11{
12
13/* 反转 */
14if( 0》 speed)
15{
16if(-MOTOR_PWM_MAX 》 speed)
17speed = -MOTOR_PWM_MAX;
18motor_pwm_control(ch, MOTOR_DIR_REVERSE, -speed);
19}
20/* 正转 */
21elseif( 0《 speed)
22{
23if(MOTOR_PWM_MAX 《 speed)
24speed = MOTOR_PWM_MAX;
25motor_pwm_control(ch, MOTOR_DIR_FORWARD, speed);
26}
27/* 停止 */
28else
29{
30motor_pwm_control(ch, MOTOR_DIR_STOP, speed);
31}
32}
第三步:把主要函数加入Finsh控制台命令中,通过命令调试控制效果
1/* FINSH 调试函数 */
2#ifdefRT_USING_FINSH
3#include《finsh.h》
4FINSH_FUNCTION_EXPORT_ALIAS(motor_pwm_control, motor_control, channel direction speed);
5FINSH_FUNCTION_EXPORT_ALIAS(motor_pwm_set, motor_set, channel speed);
6
7/* FINSH 调试命令 */
8#ifdefFINSH_USING_MSH
9
10#endif/* FINSH_USING_MSH */
11#endif/* RT_USING_FINSH */
第四步:通过Finsh控制台调试命令测试电机通道和PWM控制量
编码器数据获取
电机测速我们使用520电机自带的AB相霍尔编码器,编码器线数为390,4倍线数后轮子转一圈收到:390*4=1560个脉冲。
stm32自带AB相霍尔解码器,一个通道需要消耗一个定时器。我们主控板电机2、3、4使用的timer 3/4/5硬件解码,电机1没有接定时器,坑爹啊,那只能用外部中断根据时序解码。
编码器时序:
资料有了思路也清晰了,接下来我们要做的只是初始化一下解码器,把实时编码数读出即可
第一步:CubeMx配置解码定时器和中断
第二步:编写初始化函数和编码器数据获取函数,电机1使用中断解码,电机2、3、4使用定时器解码。(具体代码参照github上面源码,这里不再累述)
1/* TIM init */
2moto_pwm_init;
3motor_encode2_init;
4motor_encode3_init;
5motor_encode4_init;
6motor_encode_enable;
7
8LOG_I( “motor initialization completed.
”);
第三步:加入Finsh调试函数,旋转轮子查看编码值是否准确。(输入 motor_test -ge实时查看编码器值)
1MSH_CMD_EXPORT(motor_test, motor_test -ge/-q);
1![ 1565968589135](https: //github.com/bluesky-ryan/snowstorm_car/blob/master/Image/motor_test)
No.1
前言
最近有幸参加了一期RT-Thread官方发起的RT-Robot Car DIY活动,跟着大神们的步伐我也成功的做出了一辆麦克纳姆轮PS2遥控车,心里非常的Happy,特意记录了这个制作过程用作给小白们借鉴。不多逼逼了,来开始我们造车之旅。
关于选材
初次探索智能车本着节约成本和最低风险的原则,我们尽量选用现成的硬件材料。在探索成功后,学会了理解了其中的原理,再根据自己的需求完全设计自己的小车。(以下所有图片,点击即可查看大图)
小车制作仓:https://github.com/bluesky-ryan/snowstorm_car
官方Robot-car连接:https://github.com/RT-Thread-packages/rt-robot
1、主控芯片:我们选用淘宝成品主控板(主控芯片STM32F103RCT6
2、底盘:麦克纳姆轮底座,某宝多的是自行选购
3、电机:买底座基本都带电机,我们选用带AB编码器的1:30减速电机
4、遥控:普通SONY PS2遥控,30-40块钱
5、电池:选用3S 11V航模电池
6、线:USB转串口线一根
No.2
核心知识点
1.RT-Thread bsp移植。
2.STM32-CubeMXs使用。
3.RTOS使用。
4.PID控制理论。
5.麦克拉姆拉控制理论。
6.简单运动模型
开发环境
使用Keil V5作为编译器
使用 RT-Thread 最新版本
使用自己移植的 bsp
No.3
详细步骤
BSP移植
关于BSP的移植RT-Thread官网有非常详细的文档描述:
移植过程不做累述,按照官方的步骤一步一步的走即可。
先只配置控制台串口和系统呼吸灯:
console串口:UART2
系统LED灯:PD2
电机控制
主控板电机驱动芯片为TI的DIV8833芯片,一颗DIV8833芯片可以驱动两个电机,我们有4个电机用到了2个芯片。芯片采用对偶PWM方波输入驱动,频率手册没写,我们先使用10KHZ。
STM32F103RTC6高级定时器1、8都带有对偶PWM输出,我们的主控板用的高级定时器1,悲剧的是定时器1只能输出3路对偶PWM方波和1路普通PWM方波,可是我们有4个轮子,所以最后一个轮子的对偶极只能GPIO来代替控制了。
驱动逻辑表:
驱动资料有了逻辑也清晰了,我们接下要做的就只是按照要求输出几个PWM方波了
第一步:CubMX配置定时器1为PWM对偶模式
第二步:封装初始化、通道控制等电机控制接口(具体封装参照源码motor.c文件),最后给上层提供一个初始化接口,一个通道速度控制接口。(向?滑动查看全部)
1/**
2*@ingroup motor
3*
4*初始化定时器
5*@param none
6*@retrun none
7*/
8staticvoidmoto_pwm_init(void)
9{
10
11/* USER CODE BEGIN TIM1_Init 0 */
12
13/* USER CODE END TIM1_Init 0 */
14
15TIM_ClockConfigTypeDef sClockSourceConfig = { 0};
16TIM_MasterConfigTypeDef sMasterConfig = { 0};
17TIM_OC_InitTypeDef sConfigOC = { 0};
18TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = { 0};
19
20/* USER CODE BEGIN TIM1_Init 1 */
21
22/* USER CODE END TIM1_Init 1 */
23htim1.Instance = TIM1;
24htim1.Init.Prescaler = 71;
25htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
26htim1.Init.Period = MOTOR_PWM_MAX - 1;
27htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
28htim1.Init.RepetitionCounter = 0;
29htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
30if(HAL_TIM_Base_Init(&htim1) != HAL_OK)
31{
32Error_Handler;
33}
34sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
35if(HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
36{
37Error_Handler;
38}
39if(HAL_TIM_PWM_Init(&htim1) != HAL_OK)
40{
41Error_Handler;
42}
43sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
44sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
45if(HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
46{
47Error_Handler;
48}
49sConfigOC.OCMode = TIM_OCMODE_PWM1;
50sConfigOC.Pulse = 0;
51sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
52sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
53sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
54sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
55sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
56if(HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
57{
58Error_Handler;
59}
60if(HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
61{
62Error_Handler;
63}
64if(HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
65{
66Error_Handler;
67}
68if(HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
69{
70Error_Handler;
71}
72sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
73sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
74sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
75sBreakDeadTimeConfig.DeadTime = 0;
76sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
77sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_LOW;
78sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
79if(HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
80{
81Error_Handler;
82}
83/* USER CODE BEGIN TIM1_Init 2 */
84
85/* USER CODE END TIM1_Init 2 */
86HAL_TIM_MspPostInit(&htim1);
87
88HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
89HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
90HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
91HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
92
93LOG_I( “motor pwm initialization ok.
”);
94
95}
1/**
2*@ingroup motor
3*
4*控制电动机标量控制,正直表示正转,负值表示反转
5*
6*@param ch 控制通道MOTOR_CH1/TMOTOR_CH2/TMOTOR_CH3/MOTOR_CH4,可组合使用MOTOR_CH_1|MOTOR_CH_2
7*@param speed pwm控制量[-1000, 1000]
8*@retrun none
9*/
10voidmotor_pwm_set(motor_chx ch, int16_tspeed)
11{
12
13/* 反转 */
14if( 0》 speed)
15{
16if(-MOTOR_PWM_MAX 》 speed)
17speed = -MOTOR_PWM_MAX;
18motor_pwm_control(ch, MOTOR_DIR_REVERSE, -speed);
19}
20/* 正转 */
21elseif( 0《 speed)
22{
23if(MOTOR_PWM_MAX 《 speed)
24speed = MOTOR_PWM_MAX;
25motor_pwm_control(ch, MOTOR_DIR_FORWARD, speed);
26}
27/* 停止 */
28else
29{
30motor_pwm_control(ch, MOTOR_DIR_STOP, speed);
31}
32}
第三步:把主要函数加入Finsh控制台命令中,通过命令调试控制效果
1/* FINSH 调试函数 */
2#ifdefRT_USING_FINSH
3#include《finsh.h》
4FINSH_FUNCTION_EXPORT_ALIAS(motor_pwm_control, motor_control, channel direction speed);
5FINSH_FUNCTION_EXPORT_ALIAS(motor_pwm_set, motor_set, channel speed);
6
7/* FINSH 调试命令 */
8#ifdefFINSH_USING_MSH
9
10#endif/* FINSH_USING_MSH */
11#endif/* RT_USING_FINSH */
第四步:通过Finsh控制台调试命令测试电机通道和PWM控制量
编码器数据获取
电机测速我们使用520电机自带的AB相霍尔编码器,编码器线数为390,4倍线数后轮子转一圈收到:390*4=1560个脉冲。
stm32自带AB相霍尔解码器,一个通道需要消耗一个定时器。我们主控板电机2、3、4使用的timer 3/4/5硬件解码,电机1没有接定时器,坑爹啊,那只能用外部中断根据时序解码。
编码器时序:
资料有了思路也清晰了,接下来我们要做的只是初始化一下解码器,把实时编码数读出即可
第一步:CubeMx配置解码定时器和中断
第二步:编写初始化函数和编码器数据获取函数,电机1使用中断解码,电机2、3、4使用定时器解码。(具体代码参照github上面源码,这里不再累述)
1/* TIM init */
2moto_pwm_init;
3motor_encode2_init;
4motor_encode3_init;
5motor_encode4_init;
6motor_encode_enable;
7
8LOG_I( “motor initialization completed.
”);
第三步:加入Finsh调试函数,旋转轮子查看编码值是否准确。(输入 motor_test -ge实时查看编码器值)
1MSH_CMD_EXPORT(motor_test, motor_test -ge/-q);
1![ 1565968589135](https: //github.com/bluesky-ryan/snowstorm_car/blob/master/Image/motor_test)
举报