Skip to content

Commit d7060f2

Browse files
author
Peter Vaiko
committed
wip
1 parent 9655db7 commit d7060f2

4 files changed

Lines changed: 62 additions & 12 deletions

File tree

scripts/ai/turns/AITurn.lua

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ function AITurn:init(vehicle, driveStrategy, ppc, proximityController, turnConte
7474
self.state = self.states.INITIALIZING
7575
self.name = name or 'AITurn'
7676
self.blocked = false
77+
self.hasChainedAttachments = AIUtil.hasChainedAttachments(self.vehicle)
7778
end
7879

7980
function AITurn:addState(state)
@@ -233,7 +234,7 @@ function AITurn:getDriveData(dt)
233234
local maxSpeed = self:getForwardSpeed()
234235
local gx, gz, moveForwards
235236
if self.state == self.states.INITIALIZING then
236-
local rowFinishingCourse = self.turnContext:createFinishingRowCourse(self.vehicle)
237+
local rowFinishingCourse = self.turnContext:createFinishingRowCourse(self.vehicle, self:getRaiseImplementNode())
237238
self.ppc:setCourse(rowFinishingCourse)
238239
self.ppc:initialize(1)
239240
self.state = self.states.FINISHING_ROW
@@ -276,10 +277,17 @@ function AITurn:setRaiseLowerNodes()
276277
-- in headland corners, we want to stay on the field as much as possible to avoid hitting obstacles around the field.
277278
local _, backMarkerDistance = self.driveStrategy:getFrontAndBackMarkers()
278279
if backMarkerDistance < 0 then
279-
-- implement on the back of the vehicle, so before the corner, we don't work all the way to the field edge,
280-
-- stop a work width before it, then make the turn, back up until the implement reaches the field edge,
281-
-- lower, and continue on the new headland direction
282-
return self.turnContext.workEndNode, self.turnContext.workStartNode
280+
if self.hasChainedAttachments then
281+
-- implements on the back of the vehicle, we'll make a loop turn forward only, so before the corner,
282+
-- we work all the way to the headland pass edge and then make a 270 turn to continue after the corner.
283+
return self.turnContext.workEndNode, self.turnContext.workStartNode
284+
285+
else
286+
-- implement on the back of the vehicle, so before the corner, we don't work all the way to the field edge,
287+
-- stop a work width before it, then make the turn, back up until the implement reaches the field edge,
288+
-- lower, and continue on the new headland direction
289+
return self.turnContext.workEndNode, self.turnContext.workStartNode
290+
end
283291
else
284292
-- implement on the front of the vehicle, so we can work all the way to the field edge, then make the turn
285293
-- and back up only until the implement reaches the already worked part, work width from the field edge
@@ -734,10 +742,14 @@ end
734742
function CourseTurn:generateCalculatedTurn()
735743
local turnManeuver
736744
if self.turnContext:isHeadlandCorner() then
737-
-- TODO_22
738745
self:debug('This is a headland turn')
739-
turnManeuver = HeadlandCornerTurnManeuver(self.vehicle, self.turnContext, self.vehicle:getAIDirectionNode(),
746+
if self.hasChainedAttachments then
747+
turnManeuver = LoopTurnManeuver(self.vehicle, self.turnContext, self.vehicle:getAIDirectionNode(),
748+
self.turningRadius, self.workWidth, self.steeringLength)
749+
else
750+
turnManeuver = HeadlandCornerTurnManeuver(self.vehicle, self.turnContext, self.vehicle:getAIDirectionNode(),
740751
self.turningRadius, self.workWidth, self.reversingImplement, self.steeringLength)
752+
end
741753
-- adjust turn course for tight turns only for headland corners by default
742754
self.forceTightTurnOffset = self.steeringLength > 0
743755
else
@@ -1006,13 +1018,13 @@ function StartRowOnly:init(vehicle, driveStrategy, ppc, turnContext, startRowCou
10061018
-- implements are now lowering, maneuver ends when they are completely lowered
10071019
self:addState('IMPLEMENTS_LOWERING')
10081020
self.vehicle = vehicle
1009-
self.settings = vehicle:getCpSettings()
10101021
self.workStartHandler = WorkStartHandler(vehicle, driveStrategy, turnContext)
1022+
self.settings = vehicle:getCpSettings()
10111023
self.turningRadius = AIUtil.getTurningRadius(self.vehicle)
1012-
---@type AIDriveStrategyFieldWorkCourse
1013-
self.driveStrategy = driveStrategy
10141024
---@type PurePursuitController
10151025
self.ppc = ppc
1026+
---@type AIDriveStrategyFieldWorkCourse
1027+
self.driveStrategy = driveStrategy
10161028
---@type TurnContext
10171029
self.turnContext = turnContext
10181030
self.name = 'StartRowOnly'

scripts/ai/turns/TurnContext.lua

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,11 @@ function TurnContext:getLocalPositionFromTurnEnd(node)
208208
return localToLocal(node, self.vehicleAtTurnEndNode, 0, 0, 0)
209209
end
210210

211+
---@return number node pointing outwards from the corner (in a headland turn), or into the row in a 180 turn
212+
function TurnContext:getCornerOutboundNode()
213+
return self.turnEndWpNode.node
214+
end
215+
211216
-- node's position in the turn start wp node's coordinate system
212217
function TurnContext:getLocalPositionFromTurnStart(node)
213218
return localToLocal(node, self.turnStartWpNode.node, 0, 0, 0)
@@ -408,7 +413,7 @@ end
408413

409414
--- Course to finish a row before the turn, just straight ahead, ignoring the corner
410415
---@return Course
411-
function TurnContext:createFinishingRowCourse(vehicle)
416+
function TurnContext:createFinishingRowCourse(vehicle, workEndNode)
412417
local waypoints = {}
413418
-- must be at least as long as the back marker distance so we are not reaching the end of the course before
414419
-- the implement reaches the field edge (a negative backMarkerDistance means the implement is behind the
@@ -419,7 +424,7 @@ function TurnContext:createFinishingRowCourse(vehicle)
419424
-- the front marker distance would be here relevant but this is only for creating the course, where the vehicle will
420425
-- stop finishing the row and start the turn depends only on the raise implement setting.
421426
for d = 0, math.max(self.workWidth * 1.5, -self.backMarkerDistance * 1.5), 1 do
422-
local x, _, z = localToWorld(self.workEndNode, 0, 0, d)
427+
local x, _, z = localToWorld(workEndNode or self.workEndNode, 0, 0, d)
423428
table.insert(waypoints, {x = x, z = z})
424429
end
425430
return Course(vehicle, waypoints, true)

scripts/ai/turns/TurnManeuver.lua

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,24 @@ function DubinsTurnManeuver:findAnalyticPath(startNode, startXOffset, startZOffs
459459
return Course.createFromAnalyticPath(self.vehicle, path, true)
460460
end
461461

462+
---@class LoopTurnManeuver : TurnManeuver
463+
LoopTurnManeuver = CpObject(DubinsTurnManeuver)
464+
function LoopTurnManeuver:init(vehicle, turnContext, vehicleDirectionNode, turningRadius,
465+
workWidth, steeringLength)
466+
self.debugPrefix = '(LoopTurn): '
467+
TurnManeuver.init(self, vehicle, turnContext, vehicleDirectionNode, turningRadius,
468+
workWidth, steeringLength)
469+
self:debug('r=%.1f, w=%.1f, steeringLength=%.1f', turningRadius, workWidth, steeringLength)
470+
local turnEndNode, endZOffset = self.turnContext:getTurnEndNodeAndOffsets(0)
471+
local path = PathfinderUtil.findAnalyticPath(PathfinderUtil.dubinsSolver,
472+
vehicleDirectionNode, 0, 0, turnEndNode, 0, endZOffset, 5)
473+
self.course = Course.createFromNode(self.vehicle, self.vehicle:getAIDirectionNode(),
474+
0, 0, workWidth / 2, 1, false)
475+
self.course:append(Course.createFromAnalyticPath(self.vehicle, path, true))
476+
local endingTurnLength = self.turnContext:appendEndingTurnCourse(self.course, steeringLength)
477+
self:applyTightTurnOffset(endingTurnLength)
478+
end
479+
462480
-- This is an experiment to create turns with towed implements that better align with the next row.
463481
-- Instead of relying on the dynamic tight turn offset, we offset the turn end already while generating the turn
464482
-- to get the implement closer to the next row.

scripts/ai/util/AIUtil.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,3 +806,18 @@ function AIUtil.isOtherVehicleAhead(vehicle, otherVehicle)
806806
local _, _, dz = localToLocal(otherVehicle.rootNode, vehicle:getAIDirectionNode(), 0, 0, 0)
807807
return dz > (frontMarkerOffset + backMarkerOffset) / 2
808808
end
809+
810+
---@return boolean if the vehicle has multiple towed attachments connected to each other
811+
function AIUtil.hasChainedAttachments(vehicle)
812+
if vehicle.updateAIAgentAttachments then
813+
local valid = CpUtil.try(vehicle.updateAIAgentAttachments, vehicle)
814+
if valid then
815+
local attachmentChains = vehicle.spec_aiDrivable.attachmentChains
816+
if attachmentChains and #attachmentChains > 0 and #attachmentChains[1] > 1 then
817+
CpUtil.debugVehicle(CpDebug.DBG_IMPLEMENTS, vehicle, 'has %d chained attachments', #attachmentChains[1])
818+
return true
819+
end
820+
end
821+
end
822+
return false
823+
end

0 commit comments

Comments
 (0)