本文以CANOPEN协议对接电机为例,大概讲解一下驱动的原理

首先介绍一下每个文件的作用
- candev.lua 是can驱动的文件。
- opencan.lua 是opencan协议解析。
- readmotor.lua 定时发送读取电机寄存器报文。
- recmotor.lua 解析读取到的电机速度和编码器,转变成里程计的部分。[重要]
- setvel.lua 接受控制器发过来的电机速度,转换成电机需要的速度,发到电机上。[重要]
setvel.lua 部分代码
--循环发送电机速度
while true do
local data = veldev:read()
if data ~= nil and data.msg ~= nil then
rtime = 0
if data.msg.VelLeft ~= nil and data.msg.VelRight ~= nil then
local velLeft = data.msg.VelLeft * para.leftDir * para.speedratio * 10.0
local velRight = data.msg.VelRight * para.rightDir * para.speedratio * 10.0
rbasetime = rbasetime + 1
if rbasetime > 100 then
rbasetime = 0
--读取IO信息
readBase()
end
if motorLogic.setStop == true then
velLeft = 0
velRight = 0
if math.abs(os.clock() - motorLogic.disenableTime) >= 1.0 then
initMotor(0x06)
end
print("stop")
else
initMotor(0x0f)
motorLogic.disenableTime = os.clock()
end
print("motor speed : "..velLeft..'\t'..velRight)
rtime = 0
setMotoVel(velLeft,velRight)
end
else
rtime = rtime + rdelay
rbasetime = rbasetime + rdelay
--超时没有接受到电机转速,则重新加入一次域数据
if rtime > 1000 then
rtime = 0
veldev:addRegion("setwheelvel")
setMotoVel(0,0)
--读取IO信息
readBase()
if motorLogic.setStop == true then
if math.abs(os.clock() - motorLogic.disenableTime) >= 1.0 then
--超时没有接受到电机转速,则失能电机,另其可以推动
initMotor(0x06)
end
else
initMotor(0x0f)
motorLogic.disenableTime = os.clock()
end
print("timeout")
end
end
end
其中最为重要是电机转速单位
--data.msg.VelLeft为控制内部发出来的左电机速度,单位是转/秒 r/s 相对机器人方向往前为正,往后为负
--data.msg.VelRight为控制内部发出来的右电机速度,单位是转/秒 r/s 相对机器人方向往前为正,往后为负
--para.leftDir 左轮子实际的方向,该值一般是1或者-1
--para.leftDir 右轮子实际的方向,该值一般是1或者-1
--para.speedratio 轮子转速比,控制器是以机器人行驶轮的单位为标准,电机还需要通过一轮减速比限速
--60代表秒转换分钟,大部分的电机是以RPM为单位,即是单位 转每分钟
--最终得出电机的实际转速
--不同电机的单位不一样,多数驱动代码只改此部分
local velLeft = data.msg.VelLeft * para.leftDir * para.speedratio * 60.0
local velRight = data.msg.VelRight * para.rightDir * para.speedratio * 60.0
recmotor.lua部分代码,该部分则是把电机的两轮速度和编码器上传到控制器算法上。
注意单位转换,上传的单位是编码器转一圈的单位。速度则是转/秒 r/s
--获取can的数据
while true do
--读取CAN接受到的数据,100毫秒超时一次
local rarr = candev.read()
if rarr ~= nil then
for i = 1,8,1 do
if rarr[i] == nil then
rarr = nil
break
end
end
end
if rarr ~= nil and #rarr >= 8 and rarr["stdid"] >= 0x581 and rarr["stdid"] <= 0x58f then
--解析can中的数据
candel:readReceive(rarr["stdid"],rarr)
local id = candel.read.id - 0x580
--存放电机位置
if candel.read.index == 0x6063 then
motorLogic.encode[id] = candel.read.data
motorLogic.encodeRec[id] = true
end
--存放电机速度
if candel.read.index == 0x606c then
motorLogic.gvel[id] = candel.read.data
motorLogic.gvelRec[id] = true
end
end
--如果电机编码器都更新了,则更新里程计
if motorLogic.encodeRec[para.leftID] == true and
motorLogic.encodeRec[para.rightID] == true
then
motorLogic.encodeRec[para.leftID] = false
motorLogic.encodeRec[para.rightID] = false
local dis1 = limit32Bit(motorLogic.encode[para.leftID])
local dis2 = limit32Bit(motorLogic.encode[para.rightID])
dis1 = dis1 / para.speedratio
dis2 = dis2 / para.speedratio
--上传两轮里程计
local msg = {}
msg.leftEncode = dis1 * para.leftDir
msg.rightEncode = dis2 * para.rightDir
print("wheelencode : "..msg.leftEncode..'\t'..msg.rightEncode)
veldev:write("wheelencode",msg)
end
--如果电机的速度都更新了,则更新速度
if motorLogic.gvelRec[para.leftID] == true and
motorLogic.gvelRec[para.rightID] == true
then
motorLogic.gvelRec[para.leftID] = false
motorLogic.gvelRec[para.rightID] = false
motorLogic.gvel[1] = limit32Bit(motorLogic.gvel[para.leftID])
motorLogic.gvel[2] = limit32Bit(motorLogic.gvel[para.rightID])
local v1 = motorLogic.gvel[1]
local v2 = motorLogic.gvel[2]
v1 = v1 / (60.0 * para.speedratio)
v2 = v2 / (60.0 * para.speedratio)
local msg = {}
msg.VelLeft = v1 * para.leftDir
msg.VelRight = v2 * para.rightDir
--print("vel : "..msg.VelLeft..'\t'..msg.VelRight)
--上传两轮速度
veldev:write("wheelvel",msg)
end
end
以下是电机模块的配置参数describe.json
- startup 需要上电自启动的脚本
- describe 该功能包的描述
- speedratio 转速比
- encodeValue 电机转一圈的编码值
- leftID 左轮ID
- rightID 右轮ID
- leftDir 左轮运行方向
- rightDir 右轮运行方向
- bps 电机波特率
- dev 电机接到CAN1上还是CAN2上
- stopButton 急停接到的IO上[0-15]
- stopTrigger 急停触发的电平[0 1] -1失效
- resetButton 复位按钮接到的IO上[0-15]
- resetTrigger 复位按钮触发的电平[0-1] -1失效
- antiCollision 触碰防撞接到的IO上[0-15]
- antiCollisionTrigger 触碰防撞的触发电平[0-1] -1失效
- startIO 电机模块启动需要打开的IO[0-15]
- enable 是否使能改模块,true使能 false失能
{
"startup": [
"readmotor.lua",
"recmotor.lua",
"setvel.lua"
],
"describe": "OPENCAN协议",
"speedratio": 32,
"encodeValue": 10000,
"leftID": 1,
"rightID": 2,
"leftDir": 1,
"rightDir": -1,
"bps": 500,
"dev": "CAN1",
"stopButton": 15,
"stopTrigger": 0,
"resetButton": 14,
"resetTrigger": 1,
"antiCollision": -1,
"antiCollisionTrigger": 0,
"startIO": -1,
"enable": true
}