-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrobot.lua
More file actions
226 lines (196 loc) · 8.12 KB
/
robot.lua
File metadata and controls
226 lines (196 loc) · 8.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
function sysCall_init()
-- This is executed exactly once, the first time this script is executed
bubbleRobBase=sim.getObjectHandle(sim.handle_self) -- this is bubbleRob's handle
leftMotor=sim.getObjectHandle("bubbleRob_leftMotor") -- Handle of the left motor
rightMotor=sim.getObjectHandle("bubbleRob_rightMotor") -- Handle of the right motor
noseSensor=sim.getObjectHandle("bubbleRob_sensingNose") -- Handle of the proximity sensor
robot = sim.getObjectHandle('bubbleRob')
minMaxSpeed={0*math.pi/180,300*math.pi/180} -- Min and max speeds for each motor
-- Create the custom UI:
xml = '<ui title="'..sim.getObjectName(bubbleRobBase)..' speed" closeable="false" resizeable="false" activate="false">'..[[
<hslider minimum="0" maximum="100" on-change="speedChange_callback" id="1"/>
<label text="" style="* {margin-left: 300px;}"/>
</ui>
]]
ui=simUI.create(xml)
speed=(minMaxSpeed[1]+minMaxSpeed[2])*0.5
simUI.setSliderValue(ui,1,100*(speed-minMaxSpeed[1])/(minMaxSpeed[2]-minMaxSpeed[1]))
-- discs distance calculation
treshold = 0.1
discs = {
sim.getObjectHandle('Disc1'),
sim.getObjectHandle('Disc2'),
sim.getObjectHandle('Disc3'),
sim.getObjectHandle('Disc4'),
sim.getObjectHandle('Disc5'),
}
pos = { { 1, 2 },
{ -3, 4 },
{ 6, 4 },
{ 2,-3 },
{ -2,-2 },
}
timestamp = {}
for i,h in ipairs(discs) do
sim.setShapeColor(h,nil,sim.colorcomponent_ambient_diffuse,{.3,.4,.9})
timestamp[i] = nil
-- the next line set the position to pos
-- sim.setObjectPosition(h,-1,{pos[i][1], pos[i][2], 0.002})
end
end
function speedChange_callback(ui,id,newVal)
speed=minMaxSpeed[1]+(minMaxSpeed[2]-minMaxSpeed[1])*newVal/100
end
function sysCall_actuation()
sim.setJointTargetVelocity(leftMotor,speed)
sim.setJointTargetVelocity(rightMotor,speed)
-- write your code only here
-- you can create more function
-- YOU CAN use api that get the robot position
-- such as sim.getObjectPosition and sim.getObjectOrientation
----------------------------------------------------------------
-- One-time controller setup (kept across calls in globals)
----------------------------------------------------------------
if not CL then
CL = {
-- Robot geometry (tune to your model):
R = 0.04, -- [m] wheel radius
B = 0.18, -- [m] track width (wheel center-to-center)
-- PID-like gains & limits:
Kp_v = 1.2, -- linear speed gain on distance
Kp_w = 3.0, -- proportional on heading error
Ki_w = 0.4, -- integral on heading error (optional, set 0.0 to disable)
Kd_w = 0.2, -- derivative on heading error (optional, set 0.0 to disable)
V_MAX = 0.22, -- [m/s] max linear speed
W_MAX = 2.5, -- [rad/s] max angular speed
-- Behavior knobs:
SLOW_DIST = 0.35, -- [m] start tapering v near waypoint
GOAL_TOL = 0.10, -- [m] consider disc reached
FACE_ONLY = math.rad(90), -- if |heading error| > this, freeze forward v to help turning
-- State:
wp = 1, -- Disc index: 1..5
done = false,
iw = 0.0, -- integral of heading error
ew_prev = 0.0, -- previous heading error for derivative
t_prev = sim.getSimulationTime(),
}
-- Helpers local to this closure:
function CL.wrapPi(a)
while a > math.pi do a = a - 2*math.pi end
while a < -math.pi do a = a + 2*math.pi end
return a
end
function CL.clamp(x, lo, hi)
if x < lo then return lo end
if x > hi then return hi end
return x
end
function CL.wheelsFromVW(v,w)
local vl = v - 0.5*w*CL.B
local vr = v + 0.5*w*CL.B
return vl/CL.R, vr/CL.R
end
end
----------------------------------------------------------------
-- Stop if already finished
----------------------------------------------------------------
if CL.done then
sim.setJointTargetVelocity(leftMotor, 0.0)
sim.setJointTargetVelocity(rightMotor, 0.0)
return
end
----------------------------------------------------------------
-- Read robot pose (allowed in closed loop)
----------------------------------------------------------------
local p = sim.getObjectPosition(robot, -1) -- {x,y,z}
local o = sim.getObjectOrientation(robot, -1) -- {alpha,beta,gamma}
local x, y, yaw = p[1], p[2], o[3]
----------------------------------------------------------------
-- Current waypoint (disc) position
----------------------------------------------------------------
local wpH = discs[CL.wp]
local gp = sim.getObjectPosition(wpH, -1)
local gx, gy = gp[1], gp[2]
----------------------------------------------------------------
-- Errors
----------------------------------------------------------------
local dx, dy = gx - x, gy - y
local rho = math.sqrt(dx*dx + dy*dy) -- distance to goal
local th_g = math.atan2(dy, dx) -- desired heading
local e_th = CL.wrapPi(th_g - yaw) -- heading error
-- Reached current disc?
if rho < CL.GOAL_TOL then
CL.wp = CL.wp + 1
CL.iw, CL.ew_prev = 0.0, 0.0 -- reset I/D terms between waypoints
if CL.wp > #discs then
CL.done = true
sim.setJointTargetVelocity(leftMotor, 0.0)
sim.setJointTargetVelocity(rightMotor, 0.0)
return
end
-- Recompute goal using same pose this tick
wpH = discs[CL.wp]
gp = sim.getObjectPosition(wpH, -1)
gx, gy = gp[1], gp[2]
dx, dy = gx - x, gy - y
rho = math.sqrt(dx*dx + dy*dy)
th_g = math.atan2(dy, dx)
e_th = CL.wrapPi(th_g - yaw)
end
----------------------------------------------------------------
-- PID-like control law
----------------------------------------------------------------
local t = sim.getSimulationTime()
local dt = math.max(1e-3, t - CL.t_prev)
-- Linear speed from distance (with near-goal taper)
local v_cmd = CL.Kp_v * rho
if rho < CL.SLOW_DIST then
v_cmd = v_cmd * (rho / CL.SLOW_DIST)
end
v_cmd = CL.clamp(v_cmd, -CL.V_MAX, CL.V_MAX)
-- Optional: if facing away too much, stop forward motion to turn in place
if math.abs(e_th) > CL.FACE_ONLY then
v_cmd = 0.0
end
-- Heading PID (P + I + D on e_th)
CL.iw = CL.iw + e_th * dt
-- Anti-windup: limit integral term in effect (roughly)
local IW_LIM = CL.W_MAX / math.max(1e-6, CL.Ki_w)
if CL.Ki_w > 0.0 then
CL.iw = CL.clamp(CL.iw, -IW_LIM, IW_LIM)
else
CL.iw = 0.0
end
local de = (e_th - CL.ew_prev) / dt
local w_cmd = CL.Kp_w * e_th + CL.Ki_w * CL.iw + CL.Kd_w * de
w_cmd = CL.clamp(w_cmd, -CL.W_MAX, CL.W_MAX)
CL.ew_prev = e_th
CL.t_prev = t
----------------------------------------------------------------
-- Send wheel commands (only allowed actuator)
----------------------------------------------------------------
local wl, wr = CL.wheelsFromVW(v_cmd, w_cmd)
sim.setJointTargetVelocity(leftMotor, wl)
sim.setJointTargetVelocity(rightMotor, wr)
end
function sysCall_sensing()
for i,h in ipairs(discs) do
check_disc(i,h)
end
end
function check_disc(idx,handle)
r,d = sim.checkDistance(robot,handle,0)
if d[7] < treshold then
sim.setShapeColor(handle,nil,sim.colorcomponent_ambient_diffuse ,{1,.7,.4})
if (timestamp[idx] == nil) then
timestamp[idx] = sim.getSimulationTime()
print("Check point "..idx.." at "..timestamp[idx])
end
end
end
function sysCall_cleanup()
for i,h in ipairs(discs) do
sim.setShapeColor(h,nil,sim.colorcomponent_ambient_diffuse,{.3,.4,.9})
end
simUI.destroy(ui)
end