Wheel and CAN Signals#
This page gives the wheel and vehicle signals that the simulator outputs and the J1939 CAN bus of the Maxxum. It gives each parameter group with its bytes, scale and offset, the command frames, the CAN transport and the recorded CAN file.
Scope and Assumptions#
The vehicle model computes the signals at each physics step of 1/120 s. The simulator outputs them on four paths.
| Path | Content | Rate |
|---|---|---|
Session log tractor.csv |
All signals of the vehicle, the wheels and the implement. | 120 Hz |
| Sensor episode | truth.jsonl and actions.jsonl with a subset of the signals, can.jsonl with two CAN frames. |
120 Hz, 20 Hz |
| Vehicle bridge | One JSON observation with the signals that a controller needs. | 10 Hz |
| CAN bus | The same observation as J1939 frames, and command frames from a controller. | 10 Hz |
The CAN bus uses these assumptions.
- The bus is a simulator interface in the format of SAE J1939 and ISO 11783. It is not a complete network.
- The bus has no arbitration, no address claim and no transport protocol. Each frame has 8 data bytes.
- The signals have no noise. A frame contains the exact value of the model after scaling and rounding.
- The frames are for the Maxxum. The Polaris uses the drive-by-wire reports of the Drive-by-Wire and ULC page.
- The game accepts command frames from one source address only: the controller at 0x2A.
The Vehicle Bridge page gives the JSON protocol.
The Session Log page gives the columns of tractor.csv.
Symbols#
| Symbol | Quantity | Unit |
|---|---|---|
| \(r\) | Raw value of a field: an unsigned or signed integer in the frame | - |
| \(s\), \(o\) | Scale and offset of a field | unit per bit, unit |
| \(x\) | Physical value of a field | unit of the field |
| \(\kappa\) | Path curvature, positive to the left | 1/km on the bus |
| \(\delta\) | Steering angle of the bicycle model, positive to the left | rad |
| \(\delta_{max}\) | Steering limit of the vehicle, 0.6632 rad for the Maxxum | rad |
| \(L\) | Wheelbase, 2.6416 m for the Maxxum | m |
| \(v\) | Forward speed of the chassis | m/s |
| \(n_e\) | Engine speed | rev/min |
| \(u_t\), \(u_b\) | Throttle command and brake command | 0 to 1 |
| \(a_x\) | Acceleration demand of the brake frame | m/s² |
Vehicle Signals#
The table gives each signal with its source field and its name on each path. A dash shows that the path does not carry the signal.
The source fields are in FAcresVehicleTelemetry: the wheel fields in Wheels[i], the control fields in Input and the implement fields in Implement.
| Signal | Unit | Source | tractor.csv |
Sensor Episode | Vehicle Bridge | CAN |
|---|---|---|---|---|---|---|
| Wheel spin rate | rad/s | Omega |
fl_omega_radps and the three other wheels |
- | - | - |
| Wheel slip ratio | - | SlipRatio |
fl_slip |
truth.jsonl: wheels[i].slip |
slip |
0xFF12 |
| Wheel load | N | NormalN |
fl_load_n |
wheels[i].normal_n |
- | - |
| Wheel sinkage | m | SinkageM |
fl_sinkage_m |
wheels[i].sinkage_m |
- | - |
| Forward speed | m/s | SpeedMps |
speed_mps |
truth.jsonl: speed_mps |
vx |
WBSD, 0xFF11 |
| Steering angle | rad | SteeringRad |
steer_fl_rad, steer_fr_rad |
truth.jsonl: steer_rad |
steer_rad |
Guidance Machine Status, 0xFF13 |
| Steering request | rad | SteeringRadians |
steer_command_rad |
actions.jsonl: steer_request_rad |
- | can.jsonl, PGN 65280 |
| Engine speed | rev/min | Rpm |
rpm |
truth.jsonl: rpm |
rpm |
EEC1 |
| Gear and direction | - | Gear, Direction |
gear, direction |
actions.jsonl: gear, gear_direction |
- | can.jsonl, PGN 65280 |
| Throttle and brake | 0 to 1 | Throttle, Brake |
throttle, brake |
actions.jsonl |
- | can.jsonl, PGN 65280 |
| Differential lock | - | DiffLock |
diff_lock |
- | difflock |
0xFF16 |
| Hitch lever, lift angle, mode | -, deg, - | HitchLever, LiftDeg, HitchMode |
hitch_lever, hitch_lift_deg, hitch_mode |
- | hitch_lever, hitch_lift_deg, hitch_mode, raised |
- |
| PTO state, speed, power | -, rev/min, kW | bPtoOn, PtoRpm, PtoKW |
pto_on, pto_rpm, implement_pto_kw |
- | pto_on, pto_rpm, pto_kw |
0xFF18, power only |
| Tool depth | m | DepthM |
implement_depth_m |
- | implement_depth_m |
0xFF18 |
| Implement draft | N | DraftN |
implement_draft_n |
- | draft_n |
0xFF17 |
| Fuel rate, fuel used, fuel in the tank | L/h, L, L | FuelRateLph, FuelUsedL, FuelTankL |
fuel_lph, fuel_used_l, fuel_tank_l |
- | - | - |
The wheel sequence is front left, front right, rear left, rear right. The columns of tractor.csv use the prefixes fl_, fr_, rl_ and rr_.
The models of the signals are on the pages Maxxum 150 Dynamics, Tyre and Soil, Implement Mechanics and Energy and Fuel.
The ROS 2 bridge publishes the engine speed, the steering angle and the implement state of the Maxxum from the vehicle bridge.
The topics are engine_rpm, steering_angle and implement/state. The Topics page lists them.
Note
The hitch, the PTO and the fuel have no CAN command and no standard CAN frame in the simulator.
Use the key implement of the vehicle bridge, or the ROS 2 topics hitch/lever_cmd, hitch/raise_cmd and pto/cmd.
Identifiers and Fields#
A frame has a 29-bit identifier. \(p\) is the priority, PGN is the parameter group number and SA is the source address.
The middle byte of the PGN is the PDU format byte (PF). With PF less than 0xF0, the low byte of the PGN field contains the destination address.
The PGN is then the value with this byte set to zero. With PF of 0xF0 or more, the frame is a broadcast and the low byte is a part of the PGN.
The functions are J1939Id in the game and make_id, parse_id in the codec.
| Address | Node |
|---|---|
| 0x00 | Engine |
| 0x0B | Brakes |
| 0x2A | Controller: the agent that sends commands |
| 0x80 | Tractor ECU and guidance controller |
| 0xFF | All nodes |
This page numbers the bytes 0 to 7 and the bits 0 to 7 from the least significant bit. SAE and ISO documents number them 1 to 8. A field of more than one byte is little-endian. A byte that is not available is 0xFF. A field converts between its raw value and its physical value as follows.
Frames from the Tractor#
FAcresRlBridge::SendCan sends 13 frames for each observation, in the sequence of the table. An observation occurs each 0.1 s of simulation time.
| Frame | PGN | Identifier | Priority | Source | Content |
|---|---|---|---|---|---|
| EEC1 | 61444 (0xF004) | 0x0CF00400 | 3 | 0x00 | Engine speed |
| WBSD | 65096 (0xFE48) | 0x0CFE4880 | 3 | 0x80 | Speed, distance, direction |
| Guidance Machine Status | 44288 (0xAD00) | 0x0CADFF80 | 3 | 0x80 | Estimated curvature |
| Proprietary B, pose | 65296 (0xFF10) | 0x18FF1080 | 6 | 0x80 | Position |
| Proprietary B, motion | 65297 (0xFF11) | 0x18FF1180 | 6 | 0x80 | Yaw, velocity, yaw rate |
| Proprietary B, slip | 65298 (0xFF12) | 0x18FF1280 | 6 | 0x80 | Wheel slip |
| Proprietary B, soil | 65299 (0xFF13) | 0x18FF1380 | 6 | 0x80 | Soil water, steering angle |
| Proprietary B, range A | 65300 (0xFF14) | 0x18FF1480 | 6 | 0x80 | Obstacle range, sectors 0 to 7 |
| Proprietary B, range B | 65301 (0xFF15) | 0x18FF1580 | 6 | 0x80 | Obstacle range, sectors 8 to 15 |
| Proprietary B, state | 65302 (0xFF16) | 0x18FF1680 | 6 | 0x80 | Slope, differential lock, flags, counters |
| Proprietary B, farm | 65303 (0xFF17) | 0x18FF1780 | 6 | 0x80 | Harvest, crop damage, draft |
| Proprietary B, implement | 65304 (0xFF18) | 0x18FF1880 | 6 | 0x80 | Implement, tool depth, PTO power |
| Proprietary B, complete | 65311 (0xFF1F) | 0x18FF1F80 | 6 | 0x80 | Time, counter. The last frame of an observation. |
| Lockstep barrier | 65310 (0xFF1E) | 0x1CFF1E80 | 7 | 0x80 | The step counter. In lockstep only. |
The code names one suspect parameter number only: SPN 190, the engine speed. The other fields follow the layout of their parameter group. All field tables below have the same columns.
EEC1, PGN 61444#
| Bytes | Field | Type | Scale | Offset | Unit | Description |
|---|---|---|---|---|---|---|
| 3 to 4 | Engine speed, SPN 190 | uint16 | 0.125 | 0 | rev/min | \(r = \min(8 n_e, 64255)\). The maximum is 8031.875 rev/min. |
| 0 to 2, 5 to 7 | Not available | 0xFF |
WBSD, PGN 65096#
| Bytes | Field | Type | Scale | Offset | Unit | Description |
|---|---|---|---|---|---|---|
| 0 to 1 | Wheel-based machine speed | uint16 | 0.001 | 0 | m/s | The absolute value of \(v\). The maximum raw value is 64255. |
| 2 to 5 | Wheel-based machine distance | uint32 | 0.001 | 0 | m | The distance since the start, modulo 4 211 081.215 m. |
| 6 | Not available | 0xFF | ||||
| 7, bits 0 to 1 | Machine direction | 2 bits | 1 forward, 0 reverse. The other bits are 1. |
The game sets the speed of this frame to the forward speed of the chassis. It does not compute the speed from the wheel rotation. The frame thus shows no wheel slip. The distance is the sum of \(\lvert v \rvert\, \Delta t\) at each observation.
Guidance Machine Status, PGN 44288#
| Bytes | Field | Type | Scale | Offset | Unit | Description |
|---|---|---|---|---|---|---|
| 0 to 1 | Estimated curvature | uint16 | 0.25 | -8032 | 1/km | \(\kappa = 1000 \tan\delta / L\) from the actual steering angle. |
| 2 | Status | bits | 0xF4. Bits 0 to 1 are 00: the lockout is not active. Bits 2 to 3 are 01: the steering is ready. | |||
| 3 to 7 | Not available | 0xFF |
Proprietary B Frames#
| PGN | Bytes | Field | Type | Scale | Offset | Unit | Description |
|---|---|---|---|---|---|---|---|
| 65296 | 0 to 3 | Position X | int32 | 0.01 | 0 | m | Unreal world, X to the east. |
| 65296 | 4 to 7 | Position Y | int32 | 0.01 | 0 | m | Unreal world, Y to the south. |
| 65297 | 0 to 1 | Yaw | uint16 | 0.01 | 0 | deg | Unreal yaw in the range 0 to 360. 0 is east, 90 is south. |
| 65297 | 2 to 3 | Velocity, forward | int16 | 0.001 | 0 | m/s | Body axis x. |
| 65297 | 4 to 5 | Velocity, right | int16 | 0.001 | 0 | m/s | Body axis y, positive to the right. |
| 65297 | 6 to 7 | Yaw rate | int16 | 0.0001 | 0 | rad/s | Positive for a turn to the right. |
| 65298 | 0 to 7 | Wheel slip, 4 values | int16 | 0.001 | 0 | - | Front left, front right, rear left, rear right. |
| 65299 | 0 to 5 | Soil water content, 3 values | uint16 | 0.0001 | 0 | m³/m³ | Below the vehicle, 5 m ahead and 10 m ahead. |
| 65299 | 6 to 7 | Steering angle | int16 | 0.0001 | 0 | rad | Positive to the left. |
| 65300, 65301 | 0 to 7 | Obstacle range, 8 sectors each | uint8 | 0.12 | 0 | m | The maximum raw value is 250, which is 30 m. |
| 65302 | 0 to 1 | Cross slope | int16 | 0.0001 | 0 | rad | Positive when the right side is lower. |
| 65302 | 2 | Differential lock | uint8 | 1 | 0 | - | 0 off, 1 rear, 2 front and rear. |
| 65302 | 3 | Flags | bits | Bit 0 worker struck, bit 1 reset pending, bit 2 safety brake, bit 3 agent in control. | |||
| 65302 | 4 to 5 | Episode counter | uint16 | 1 | 0 | - | The resets that the agent requested. |
| 65302 | 6 | Workers | uint8 | 1 | 0 | - | The workers that the vehicle bridge placed. |
| 65302 | 7 | Resets applied | uint8 | 1 | 0 | - | The low byte of the reset counter. |
| 65303 | 0 to 1 | Bunker mass | uint16 | 1 | 0 | kg | |
| 65303 | 2 to 3 | Harvested mass | uint16 | 1 | 0 | kg | |
| 65303 | 4 to 5 | Crushed crop area | uint16 | 0.1 | 0 | m² | |
| 65303 | 6 to 7 | Implement draft | uint16 | 10 | 0 | N | |
| 65304 | 0 | Implement kind | uint8 | 1 | 0 | - | 0 to 7, see below. 0xFF with no implement. |
| 65304 | 1 to 2 | Mean tool depth | uint16 | 0.001 | 0 | m | The maximum raw value is 64255. |
| 65304 | 3 to 4 | PTO shaft power | uint16 | 0.1 | 0 | kW | The maximum raw value is 64255. |
| 65311 | 0 to 3 | Physics time | uint32 | 0.001 | 0 | s | |
| 65311 | 4 to 5 | Time since the reset | uint16 | 0.01 | 0 | s | The maximum is 655.35 s. |
| 65311 | 6 | Sequence counter | uint8 | 1 | 0 | - | Increases by 1 for each observation. |
| 65310 | 0 to 7 | World step | uint64 | 1 | 0 | step | The step that a step request reached. |
The implement kinds are 0 cultivator, 1 chisel_plow, 2 disc_harrow, 3 seed_drill, 4 mounted_sprayer, 5 loader_bale_fork, 6 backhoe_attachment and 7 square_baler.
The obstacle range is not the LiDAR sensor. It is the nearest object in each of 16 sectors of 11.25° in front of the vehicle.
Sector \(k\) covers the bearings from \(-90° + 11.25° k\) to \(-90° + 11.25° (k + 1)\) from the heading. The maximum range is 30 m.
A controller collects the frames and uses them when frame 65311 arrives. The class ObservationAssembler of the codec does this.
Command Frames#
FAcresRlBridge::HandleCanFrame decodes four parameter groups. The destination address must be the address of the table or 0xFF.
| Frame | PGN | Identifier | Priority | Source | Destination | Effect |
|---|---|---|---|---|---|---|
| Guidance System Command | 44032 (0xAC00) | 0x0CAC802A | 3 | 0x2A | 0x80 | Steering |
| TSC1 | 0 (0x0000) | 0x0C00002A | 3 | 0x2A | 0x00 | Throttle |
| XBR | 1024 (0x0400) | 0x0C040B2A | 3 | 0x2A | 0x0B | Brake |
| Proprietary A | 61184 (0xEF00) | 0x18EF802A | 6 | 0x2A | 0x80 | Differential lock, reset, workers, HUD |
The game does not examine the priority. It ignores a frame with less than 8 data bytes and a frame from a different source address.
Guidance System Command, PGN 44032#
| Bytes | Field | Type | Scale | Offset | Unit | Description |
|---|---|---|---|---|---|---|
| 0 to 1 | Curvature command | uint16 | 0.25 | -8032 | 1/km | Positive to the left. |
| 2, bits 0 to 1 | Steering command status | 2 bits | 1: the controller intends to steer. Other values put the wheels in the centre. |
One bit of the curvature is 0.25 km⁻¹. Near the centre this is a steering angle of 0.00066 rad for the Maxxum.
TSC1, PGN 0#
| Bytes | Field | Type | Scale | Offset | Unit | Description |
|---|---|---|---|---|---|---|
| 0, bits 0 to 1 | Override control mode | 2 bits | 0 no override, 1 speed control, 2 torque control. | |||
| 1 to 2 | Requested speed | uint16 | 0.125 | 0 | rev/min | Used in the mode 1. |
| 3 | Requested torque | uint8 | 1 | -125 | % | Used in the mode 2. |
The torque mode is thus the throttle pedal. The speed mode is a proportional governor with a band of 400 rev/min.
XBR, PGN 1024#
| Bytes | Field | Type | Scale | Offset | Unit | Description |
|---|---|---|---|---|---|---|
| 0 to 1 | External acceleration demand | uint16 | 0.00048828125 | -15.687 | m/s² | The scale is 1/2048. |
| 2, bits 6 to 7 | XBR control mode | 2 bits | 0: the override is off. |
A demand of -4 m/s² is the full brake.
Proprietary a, PGN 61184#
Byte 0 selects the message. The unused bytes are 0xFF.
| Byte 0 | Bytes | Field | Type | Scale | Offset | Unit | Description |
|---|---|---|---|---|---|---|---|
| 0x01 | 1 | Differential lock | uint8 | 0 off, 1 rear, 2 front and rear. Other values keep the state. | |||
| 0x01 | 2 | Harvester | uint8 | 0 off, 1 on. Other values keep the state. | |||
| 0x01 | 3 | Reset | uint8 | 1: reset the vehicle to the staged pose. | |||
| 0x01 | 4, 5 | Place workers | uint8 | Byte 4 is 1: place the number of staged workers in byte 5. | |||
| 0x01 | 6 | Restore field | uint8 | 1 with a reset: remove the marks of the field below the pose. | |||
| 0x10 | 1 to 4 | Reset position X | int32 | 0.01 | 0 | m | Unreal world, X to the east. |
| 0x11 | 1 to 4 | Reset position Y | int32 | 0.01 | 0 | m | Unreal world, Y to the south. |
| 0x12 | 1 to 2 | Reset yaw | uint16 | 0.01 | 0 | deg | Unreal yaw, 0 to 360. |
| 0x12 | 3 to 4 | Reset speed | uint16 | 0.001 | 0 | m/s | The start speed. The game limits it to 8 m/s. |
| 0x21 | 1 | Worker index | uint8 | 0 to 7. | |||
| 0x21 | 2 to 4 | Worker X | int24 | 0.01 | 0 | m | |
| 0x21 | 5 to 7 | Worker Y | int24 | 0.01 | 0 | m | |
| 0x30 | 1 to 4 | Reward | float32 | The value that the HUD shows. | |||
| 0x30 | 5 | Success rate | uint8 | 1 | 0 | % | 0 to 100. 0xFF shows no rate. |
A reset with no staged position goes to the spawn pose of the session. The messages 0x10 and 0x11 stage the position.
A reset starts a new episode. The marks on the fields stay unless byte 6 is 1.
A front and rear lock needs a driven front axle. Workers need the option -NpcWorkers.
Timing and Safety#
| Rule | Value | Source |
|---|---|---|
| Observation period | 0.1 s of simulation time. The test uses a tolerance of half a physics step. | ObservationPeriodS |
| Controller present | A command frame arrived in the last 3 s of wall-clock time. | CanPresenceS |
| Safety brake | No steering, throttle or brake frame for 1 s of wall-clock time. The game then sets the brake to 1. | ActionTimeoutS |
| Gear of the agent | F10, forward. | AgentGear |
| Lockstep | The wall-clock limits do not apply. A command applies at the next physics step. | FAcresSimControl::IsLockstep |
While a controller is present, its commands replace the keyboard for the throttle, the brake, the steering, the gear and the differential lock. The frames Guidance System Command, TSC1 and XBR are the commands that drive the vehicle. A Proprietary A frame keeps the controller present but does not reset the 1 s timer.
A receiver thread reads the transports each millisecond and records the arrival time of each frame. A command that drives the vehicle then applies at the physics step that agrees with its arrival time. The Time Stepping and Determinism page gives this rule. The game sends the observation one time for each rendered frame at most. The period is thus 0.1 s or a small amount more. A frame rate below 10 Hz decreases the observation rate.
CAN Transport#
The game has two transports for the same frames. Both transports can be open at the same time.
| Transport | Option | Description |
|---|---|---|
| CAN on UDP | -RlCanUdp=<port> |
One frame in each UDP datagram on the address 127.0.0.1. |
| SocketCAN | -RlCan=<interface> |
A raw socket on a Linux CAN interface, for example vcan0. |
The two options apply to agent 0. A different agent sets the key can_udp in the agents file.
The transports are for Linux only. On other platforms the game writes ACRES_RL_CAN_UNSUPPORTED to the log.
A datagram is the 16 bytes of the Linux structure can_frame.
| Offset | Type | Field |
|---|---|---|
| 0 | uint32, little-endian | The identifier with the flag 0x80000000 for a 29-bit identifier. |
| 4 | uint8 | The data length, 8. |
| 5 | 3 bytes | Padding. |
| 8 | 8 bytes | The data. |
The game ignores frames with an 11-bit identifier, remote frames and error frames. The UDP socket of the game has no fixed peer. The game sends its frames to the address of the last datagram that it received. Thus a controller must send one datagram before it gets frames. The SocketCAN transport sends frames at all times.
The ROS 2 bridge connects to the UDP port with the parameter can_udp_port.
| Topic | Direction | Message | Description |
|---|---|---|---|
can/rx |
Published | can_msgs/Frame |
The frames from the tractor. The stamp is the simulation time and the frame name is can. |
can/tx |
Subscribed | can_msgs/Frame |
The frames to the tractor. |
The two topics have a queue depth of 500, as the nodes of ros2_socketcan have.
The bridge sends a keep-alive frame at the start and then each second: a Proprietary A frame from the source address 0xFE with 0xFF data.
The game learns the peer from this frame and ignores its content.
In lockstep, the game sends the barrier frame 65310 after all frames of a step request.
The ROS 2 bridge counts this frame for the step reply and does not publish it on can/rx.
The Lockstep Stepping tutorial shows the barrier.
Codec#
The codec exists in Python and in C++. The two give the same bytes, and a test compares them.
| Language | File | Use |
|---|---|---|
| Python | ROS/acres_sim/acres_sim/j1939.py |
import acres_sim.j1939 |
| C++ | ROS/acres_sim/include/acres_sim/j1939.hpp |
A header without ROS 2, namespace acres_sim::j1939 |
Expected Result
These are the 13 frames of the first observation of a session, as describe shows them. The Maxxum is at rest at the default spawn.
0CF00400 [8] FF FF FF EF 18 FF FF FF EEC1 00->FF rpm=798
0CFE4880 [8] 00 00 00 00 00 00 FF FD WBSD 80->FF speed=0.000m/s dir=fwd
0CADFF80 [8] 80 7D F4 FF FF FF FF FF GMS 80->FF est. curvature=+0.00/km
18FF1080 [8] E1 F0 FF FF E3 36 00 00 Pose 80->FF x=-38.71 y=140.51
18FF1180 [8] 78 69 00 00 00 00 00 00 Motion 80->FF yaw=270.00 vx=0.000 vy=0.000 r=+0.0000
18FF1280 [8] 00 00 00 00 00 00 00 00 Slip 80->FF slip=+0.000,+0.000,+0.000,+0.000
18FF1380 [8] D0 07 D0 07 D0 07 00 00 Soil 80->FF theta=0.200/0.200/0.200 steer=+0.0000rad
18FF1480 [8] FA FA FA FA FA FA FA FA LidarA 80->FF m=30.0,30.0,30.0,30.0,30.0,30.0,30.0,30.0
18FF1580 [8] FA BC D5 F9 FA FA FA FA LidarB 80->FF m=30.0,22.6,25.6,29.9,30.0,30.0,30.0,30.0
18FF1680 [8] 00 00 00 08 00 00 00 00 State 80->FF slope=+0.0000 difflock=0 flags=1000 episode=0 workers=0
18FF1780 [8] 00 00 00 00 00 00 00 00 Farm 80->FF
18FF1880 [8] FF 00 00 00 00 FF FF FF Implement 80->FF
18FF1F80 [8] 2A 00 00 00 04 00 00 FF ObsDone 80->FF t=0.04s ep_t=0.04s seq=0
The codec rounds a half to the even value, as Python does. The game rounds a half up. The two differ only for a value at the middle of a bit.
Example: A Controller on the UDP Transport#
This example drives the Maxxum on a circle with CAN frames only. It does not use ROS 2.
-
Start the game with the CAN transport on UDP port 5556.
Expected Result
The log contains the line
ACRES_RL_CAN_UDP_OPEN port=5556. -
Write this program into the file
can_client.py.import socket import struct import time from acres_sim import j1939 game = ("127.0.0.1", 5556) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(0.02) commands = [j1939.encode_gsc(50.0), j1939.encode_tsc1_torque(0.45), j1939.encode_xbr(0.0)] assembler = j1939.ObservationAssembler() next_send = 0.0 while True: if time.monotonic() >= next_send: for can_id, data in commands: sock.sendto(struct.pack("<IB3x8s", can_id | 0x80000000, 8, data), game) next_send = time.monotonic() + 0.1 try: raw = sock.recv(16) except (socket.timeout, ConnectionRefusedError): continue can_id, _, data = struct.unpack("<IB3x8s", raw) observation = assembler.feed(can_id & 0x1FFFFFFF, data) if observation and observation["sequence"] % 20 == 0: print(f'{observation["t"]:6.1f} s {observation["speed"]:5.2f} m/s {observation["curvature_km"]:5.1f} 1/km', flush=True) -
Start the program in a second terminal.
-
Stop the program with Ctrl+C.
Expected Result
The game sets the brake after 1 s without commands. After 3 s the keyboard has the control again.
The program sends the three command frames each 0.1 s. The curvature command is 50 km⁻¹, which is a circle of 20 m radius. The estimated curvature of the status frame goes to the commanded value when the steering reaches its angle. At the default spawn, the circle meets a building after approximately 45 s. The speed then goes to zero.
Recorded CAN File#
The sensor recorder writes can.jsonl in the episode folder. It is independent of the live bus.
It writes two rows at the rate can.hz, with no latency. The Polaris overlay sets can.enabled to false.
| Row | Identifier | PGN | Bytes | Field | Type | Scale | Offset | Unit |
|---|---|---|---|---|---|---|---|---|
| 1 | 0x0CF00480 | 61444 | 3 to 4 | Engine speed, SPN 190 | uint16 | 0.125 | 0 | rev/min |
| 2 | 0x18FF0080 | 65280 | 0 | Throttle | uint8 | 0.4 | 0 | % |
| 2 | 0x18FF0080 | 65280 | 1 | Brake | uint8 | 0.4 | 0 | % |
| 2 | 0x18FF0080 | 65280 | 2 to 3 | Steering request | uint16 | 0.01 | -327.68 | deg |
| 2 | 0x18FF0080 | 65280 | 4 | Direction | uint8 | 1 forward, 2 reverse |
The other bytes are 255. PGN 65280 is a format of ACRES. It is not a standard actuation message of a tractor. Row 1 has the source address 0x80 in the file. The live bus sends EEC1 from the address 0x00.
| Key | Type | Description |
|---|---|---|
time_s |
number | The episode time of the sample. |
extended |
boolean | true: a 29-bit identifier. |
id_hex |
string | The identifier as 8 hexadecimal digits. |
pgn |
integer | The parameter group number. |
data |
array of 8 | The data bytes as integers. |
direction |
string | feedback for row 1 and command for row 2. |
source |
string | Row 2 only. The control source, for example manual, replay or scripted_test. |
mapping |
string | A description of the frame. |
These are the first two rows of a recording. The engine speed is \(6395 \times 0.125 = 799.4\) rev/min and the brake is 100 %.
{"time_s": 0.0, "extended": true, "id_hex": "0CF00480", "pgn": 61444, "data": [255, 255, 255, 251, 24, 255, 255, 255], "direction": "feedback", "mapping": "EEC1 SPN190 only; remaining bytes unavailable"}
{"time_s": 0.0, "extended": true, "id_hex": "18FF0080", "pgn": 65280, "data": [0, 250, 0, 128, 1, 255, 255, 255], "direction": "command", "source": "scripted_test", "mapping": "ACRES proprietary v1; NOT a standard tractor actuation PGN"}
Quantisation and Noise#
The CAN signals have no noise model and no latency model. The column "Scale" of each field is its quantisation step.
| Effect | Rule |
|---|---|
| Rounding | To the nearest raw value. A half goes up. |
| Limits | The raw value stays in the range of its type. The engine speed and the speed of WBSD stop at 64255. |
| Angle | The yaw of frame 65297 is modulo 360°. |
| Counters | The distance, the physics time, the episode counter and the sequence counter go back to zero at the end of their range. |
| Time since the reset | The value stops at 655.35 s. |
| Sample time | A frame contains the state at the game frame that sends it. The frame has no time stamp of its own. |
The file can.jsonl uses the same rounding. Its rows show the state at the physics step of time_s.
Parameters#
| Name | Type | Unit | Default | Description |
|---|---|---|---|---|
-RlCanUdp= |
integer | off | The UDP port of the CAN transport for agent 0. | |
-RlCan= |
string | off | The SocketCAN interface for agent 0. | |
can_udp |
integer | off | The key of an agent in the agents file: the UDP port of that agent. | |
can_udp_port |
integer | 0 | The parameter of the ROS 2 bridge: the UDP port of the game. 0 sets the topics off. | |
can.enabled |
boolean | true | The key of sensors.json: the recorder writes can.jsonl. |
|
can.hz |
number | Hz | 20 | The key of sensors.json: the rate of can.jsonl. Range 1 to 120. |
-SensorNoCan |
flag | Sets can.jsonl off. |
The constants of the table "Timing and Safety" are in the code. No configuration file changes them.
Code Map#
| Item | File | Function |
|---|---|---|
| Encoder of the tractor frames | Acres/Source/Acres/AcresRlBridge.cpp |
FAcresRlBridge::SendCan, J1939Id |
| Decoder of the command frames | AcresRlBridge.cpp |
FAcresRlBridge::HandleCanFrame |
| Observation, obstacle range, distance | AcresRlBridge.cpp |
Observe, TraceLidar |
| Transports | AcresRlBridge.cpp |
ReadCan, WriteCan, the constructor |
| Controller present, safety brake | AcresRlBridge.cpp |
CanInControl, ActionStale, CurrentControls |
| Lockstep barrier | AcresRlBridge.cpp |
SendBarrier |
| Options | Acres/Source/Acres/AcresVehicle.cpp |
AAcresVehiclePawn::BeginPlay |
| Recorded CAN rows | Acres/Source/Acres/AcresSensors.cpp |
FAcresSensorRecorder::PhysicsSample |
| Session log | Acres/Source/Acres/AcresSessionLog.cpp |
FAcresSessionLog::Open, Push |
| Codec, Python | ROS/acres_sim/acres_sim/j1939.py |
encode_*, decode_*, ObservationAssembler, describe |
| Codec, C++ | ROS/acres_sim/include/acres_sim/j1939.hpp |
The same names |
| UDP client of the ROS 2 bridge | ROS/acres_sim/src/stream.cpp |
UdpCanClient, write_can_frame, read_can_frame |
| CAN topics | ROS/acres_sim/src/vehicle_bridge.cpp, conversions.cpp |
VehicleBridge::on_can, to_can_msg, from_can_msg |
| Tests | ROS/acres_sim/test/test_j1939.cpp, test_j1939_golden.py, j1939_golden.cpp |
Identifiers, round trips, C++ against Python |
| Check with the game | Tools/SimControl/can_over_ros_check.py |
A guidance command on can/tx moves the tractor |
Limitations#
- The bus has the frames of this page only. It has no address claim, no requests and no diagnostic messages.
- The wheel-based speed is the speed of the chassis. The simulator has no model of a wheel speed sensor with slip and pulses.
- The hitch, the PTO and the fuel are not on the bus as standard ISO 11783 frames.
- The speed mode of TSC1 is a proportional governor, not the engine controller of the vehicle model.
- The brake command is a linear map of the acceleration demand. It is not a brake controller with feedback.
- The observation rate depends on the frame rate of the game when the frame rate is below 10 Hz.
- The frame 65304 is in the game and in the decoder of the codec. The function
encode_observationof the codec does not make it. - ACRES Core has no CAN bus.
References#
- SAE International. SAE J1939-21: Data Link Layer and SAE J1939-71: Vehicle Application Layer (EEC1, TSC1, XBR).
- International Organization for Standardization. ISO 11783-7: Tractors and machinery for agriculture and forestry, Serial control and communications data network, Part 7: Implement messages application layer (WBSD, guidance messages).
- Linux kernel documentation. SocketCAN: Controller Area Network, the structure
can_frame.